[MERGE] merged with trunk development

bzr revid: hmo@tinyerp.com-20111026163532-c2w6yregezsqlmgu
This commit is contained in:
Harry (OpenERP)
2011-10-26 22:05:32 +05:30
67 changed files with 3859 additions and 1486 deletions
+40 -12
View File
@@ -243,7 +243,8 @@ class account_account(osv.osv):
'balance': "COALESCE(SUM(l.debit),0) " \
"- COALESCE(SUM(l.credit), 0) as balance",
'debit': "COALESCE(SUM(l.debit), 0) as debit",
'credit': "COALESCE(SUM(l.credit), 0) as credit"
'credit': "COALESCE(SUM(l.credit), 0) as credit",
'foreign_balance': "COALESCE(SUM(l.amount_currency), 0) as foreign_balance",
}
#get all the necessary accounts
children_and_consolidated = self._get_children_and_consol(cr, uid, ids, context=context)
@@ -270,7 +271,7 @@ class account_account(osv.osv):
# ON l.account_id = tmp.id
# or make _get_children_and_consol return a query and join on that
request = ("SELECT l.account_id as id, " +\
', '.join(map(mapping.__getitem__, field_names)) +
', '.join(map(mapping.__getitem__, mapping.keys())) +
" FROM account_move_line l" \
" WHERE l.account_id IN %s " \
+ filters +
@@ -289,7 +290,7 @@ class account_account(osv.osv):
sums = {}
currency_obj = self.pool.get('res.currency')
while brs:
current = brs[0]
current = brs.pop(0)
# can_compute = True
# for child in current.child_id:
# if child.id not in sums:
@@ -299,7 +300,6 @@ class account_account(osv.osv):
# except ValueError:
# brs.insert(0, child)
# if can_compute:
brs.pop(0)
for fn in field_names:
sums.setdefault(current.id, {})[fn] = accounts.get(current.id, {}).get(fn, 0.0)
for child in current.child_id:
@@ -307,6 +307,16 @@ class account_account(osv.osv):
sums[current.id][fn] += sums[child.id][fn]
else:
sums[current.id][fn] += currency_obj.compute(cr, uid, child.company_id.currency_id.id, current.company_id.currency_id.id, sums[child.id][fn], context=context)
# as we have to relay on values computed before this is calculated separately than previous fields
if current.currency_id and current.exchange_rate and \
('adjusted_balance' in field_names or 'unrealized_gain_loss' in field_names):
# Computing Adjusted Balance and Unrealized Gains and losses
# Adjusted Balance = Foreign Balance / Exchange Rate
# Unrealized Gains and losses = Adjusted Balance - Balance
adj_bal = sums[current.id].get('foreign_balance', 0.0) / current.exchange_rate
sums[current.id].update({'adjusted_balance': adj_bal, 'unrealized_gain_loss': adj_bal - sums[current.id].get('balance', 0.0)})
for id in ids:
res[id] = sums.get(id, null_result)
else:
@@ -340,9 +350,10 @@ class account_account(osv.osv):
accounts = self.browse(cr, uid, ids, context=context)
for account in accounts:
level = 0
if account.parent_id:
obj = self.browse(cr, uid, account.parent_id.id)
level = obj.level + 1
parent = account.parent_id
while parent:
level += 1
parent = parent.parent_id
res[account.id] = level
return res
@@ -367,8 +378,8 @@ class account_account(osv.osv):
move_obj = self.pool.get('account.move.line')
move_id = move_obj.search(cr, uid, [
('journal_id','=',jids[0]),
('period_id','=',pids[0]),
('journal_id','=',jids[0]),
('period_id','=',pids[0]),
('account_id','=', account_id),
(name,'>', 0.0),
('name','=', _('Opening Balance'))
@@ -418,7 +429,14 @@ class account_account(osv.osv):
'balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Balance', multi='balance'),
'credit': fields.function(__compute, fnct_inv=_set_credit_debit, digits_compute=dp.get_precision('Account'), string='Credit', multi='balance'),
'debit': fields.function(__compute, fnct_inv=_set_credit_debit, digits_compute=dp.get_precision('Account'), string='Debit', multi='balance'),
'foreign_balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Foreign Balance', multi='balance',
help="Total amount (in Secondary currency) for transactions held in secondary currency for this account."),
'adjusted_balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Adjusted Balance', multi='balance',
help="Total amount (in Company currency) for transactions held in secondary currency for this account."),
'unrealized_gain_loss': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Unrealized Gain or Loss', multi='balance',
help="Value of Loss or Gain due to changes in exchange rate when doing multi-currency transactions."),
'reconcile': fields.boolean('Allow Reconciliation', help="Check this box if this account allows reconciliation of journal items."),
'exchange_rate': fields.related('currency_id', 'rate', type='float', string='Exchange Rate', digits=(12,6)),
'shortcut': fields.char('Shortcut', size=12),
'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
'account_id', 'tax_id', 'Default Taxes'),
@@ -436,7 +454,10 @@ class account_account(osv.osv):
'manage this. So if you import from another software system you may have to use the rate at date. ' \
'Incoming transactions always use the rate at date.', \
required=True),
'level': fields.function(_get_level, string='Level', store=True, type='integer'),
'level': fields.function(_get_level, string='Level', method=True, type='integer',
store={
'account.account': (_get_children_and_consol, ['level', 'parent_id'], 10),
}),
}
_defaults = {
@@ -884,9 +905,16 @@ class account_fiscalyear(osv.osv):
return True
def find(self, cr, uid, dt=None, exception=True, context=None):
if context is None: context = {}
if not dt:
dt = time.strftime('%Y-%m-%d')
ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
args = [('date_start', '<=' ,dt), ('date_stop', '>=', dt)]
if context.get('company_id', False):
args.append(('company_id', '=', context['company_id']))
else:
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
args.append(('company_id', '=', company_id))
ids = self.search(cr, uid, args, context=context)
if not ids:
if exception:
raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
@@ -930,7 +958,7 @@ class account_period(osv.osv):
_sql_constraints = [
('name_company_uniq', 'unique(name, company_id)', 'The name of the period must be unique per company!'),
]
def _check_duration(self,cr,uid,ids,context=None):
obj_period = self.browse(cr, uid, ids[0], context=context)
if obj_period.date_stop < obj_period.date_start:
+1
View File
@@ -43,6 +43,7 @@
<menuitem id="menu_finance_statistic_report_statement" name="Statistic Reports" parent="menu_finance_reporting" sequence="300"/>
<menuitem id="next_id_22" name="Partners" parent="menu_finance_generic_reporting" sequence="1"/>
<menuitem id="menu_multi_currency" name="Multi-Currencies" parent="menu_finance_generic_reporting" sequence="10"/>
<menuitem
parent="account.menu_finance_legal_statement"
id="final_accounting_reports"
+34
View File
@@ -295,6 +295,40 @@
<field name="domain">[('parent_id','=',False)]</field>
</record>
<record id="view_account_gain_loss_tree" model="ir.ui.view">
<field name="name">Unrealized Gain or Loss</field>
<field name="model">account.account</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Unrealized Gains and losses">
<field name="code"/>
<field name="name"/>
<field name="currency_id"/>
<field name="exchange_rate"/>
<field name="foreign_balance"/>
<field name="adjusted_balance"/>
<field name="balance"/>
<field name="unrealized_gain_loss"/>
</tree>
</field>
</record>
<record id="action_account_gain_loss" model="ir.actions.act_window">
<field name="name">Unrealized Gain or Loss</field>
<field name="res_model">account.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_account_gain_loss_tree"/>
<field name="domain">[('currency_id','!=',False)]</field>
<field name="help">When doing multi-currency transactions, you may loose or gain some amount due to changes of exchange rate. This menu gives you a forecast of the Gain or Loss you'd realized if those transactions were ended today. Only for accounts having a secondary currency set.</field>
</record>
<menuitem
name="Unrealized Gain or Loss"
action="action_account_gain_loss"
id="menu_unrealized_gains_losses"
parent="account.menu_multi_currency"/>
<!--
Journal
+1 -1
View File
@@ -40,7 +40,7 @@
<form string="Account Board">
<hpaned>
<child1>
<action colspan="4" height="160" width="400" name="%(account.action_invoice_tree1)d" string="Customer Invoices to Approve" domain="[('state','=','draft'),('type','=','out_invoice')]"/>
<action colspan="4" height="160" width="400" name="%(account.action_invoice_tree1)d" string="Customer Invoices to Approve" domain="[('state','in',('draft','proforma2')), ('type','=','out_invoice')]"/>
<action colspan="4" height="160" width="400" name="%(action_company_analysis_tree)d" string="Company Analysis" groups="account.group_account_manager"/>
</child1>
<child2>
+1 -1
View File
@@ -19,7 +19,7 @@
##############################################################################
{
"name" : "Anglo-Saxon Accouting",
"name" : "Anglo-Saxon Accounting",
"version" : "1.2",
"author" : "OpenERP SA, Veritos",
"website" : "http://tinyerp.com - http://veritos.nl",
+1 -1
View File
@@ -20,7 +20,7 @@
##############################################################################
{
'name': 'Followups Management',
'name': 'Followup Management',
'version': '1.0',
'category': 'Accounting & Finance',
'complexity': "normal",
@@ -43,7 +43,7 @@
<form string="Accounting Voucher">
<group col="6" colspan="4">
<field name="partner_id" required="1" on_change="onchange_journal_voucher(line_ids, tax_id, amount, partner_id, journal_id, type)"/>
<field name="date" on_change="onchange_date(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="date" on_change="onchange_date(date, currency_id, amount)"/>
<field name="journal_id" widget="selection" select="1" on_change="onchange_journal_voucher(line_ids, tax_id, amount, partner_id, journal_id, type)"/>
<field name="type" required="1"/>
<field name="name" colspan="2"/>
+13 -8
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: 2011-01-11 11:14+0000\n"
"PO-Revision-Date: 2011-06-27 10:43+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"PO-Revision-Date: 2011-10-25 11:10+0000\n"
"Last-Translator: Jussi Mikkola <Unknown>\n"
"Language-Team: Finnish <fi@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: 2011-10-19 05:47+0000\n"
"X-Generator: Launchpad (build 14157)\n"
"X-Launchpad-Export-Date: 2011-10-26 05:13+0000\n"
"X-Generator: Launchpad (build 14189)\n"
#. module: association
#: field:profile.association.config.install_modules_wizard,wiki:0
@@ -72,8 +72,8 @@ msgid ""
"Tracks and manages employee expenses, and can automatically re-invoice "
"clients if the expenses are project-related."
msgstr ""
"Seuraa ja hallitsee työntekijäkuluja ja voi automaattisesti laskuttaa "
"asiakasta, jos kulut liittyvät projektiin."
"Seuraa ja hallinnoi työntekijöiden matkalaskuja ja osaa myös automaattisesti "
"(kautta)laskuttaa projekteihin liittyvät kulut asiakkaalta."
#. module: association
#: help:profile.association.config.install_modules_wizard,project_gtd:0
@@ -81,11 +81,14 @@ msgid ""
"GTD is a methodology to efficiently organise yourself and your tasks. This "
"module fully integrates GTD principle with OpenERP's project management."
msgstr ""
"Kerralla valmista (Getting things Done, GTD) on menetelmä tehokkaaseen "
"ajanhallintaan. Moduuli integroi saumattomasti GTD:n periaatteet OpenERPin "
"projektihallintaan."
#. module: association
#: view:profile.association.config.install_modules_wizard:0
msgid "Resources Management"
msgstr "Resurssienhallinta"
msgstr "Resurssien hallinta"
#. module: association
#: model:ir.module.module,shortdesc:association.module_meta_information
@@ -95,7 +98,7 @@ msgstr "Yhdistysprofiili"
#. module: association
#: field:profile.association.config.install_modules_wizard,hr_expense:0
msgid "Expenses Tracking"
msgstr "Kulujenseuranta"
msgstr "Kulujen seuranta"
#. module: association
#: model:ir.actions.act_window,name:association.action_config_install_module
@@ -109,6 +112,8 @@ msgid ""
"Lets you create wiki pages and page groups in order to keep track of "
"business knowledge and share it with and between your employees."
msgstr ""
"Mahdollistaa Wiki-sivujen luonnin ja sivujen kokoamisen ryhmiksi, mikä "
"tehostaa työntekijöiden välistä liiketoiminnan tietämyksen hallintaa."
#. module: association
#: help:profile.association.config.install_modules_wizard,project:0
+4 -4
View File
@@ -39,7 +39,7 @@ DEFAULT_MODULES = {
'Accounting & Finance' : ['account'],
'Purchase Management' : ['purchase'],
'Human Resources' : ['hr',],
'Point of Sales' : ['pos',],
'Point of Sales' : ['point_of_sale',],
'Marketing' : ['marketing',],
}
@@ -58,7 +58,7 @@ class base_setup_installer(osv.osv_memory):
if fields is None:
fields = {}
fields = {}
fields = {}
category_proxy = self.pool.get('ir.module.category')
domain = [('parent_id', '=', False),
('name', '!=', 'Uncategorized'),
@@ -389,14 +389,14 @@ class user_preferences_config(osv.osv_memory):
('extended','Extended')],
'Interface', required=True, help= "If you use OpenERP for the first time we strongly advise you to select the simplified interface, which has less features but is easier. You can always switch later from the user preferences." ),
'menu_tips': fields.boolean('Display Tips', help="Check out this box if you want to always display tips on each menu action"),
}
_defaults={
'view' : lambda self,cr,uid,*args: self.pool.get('res.users').browse(cr, uid, uid).view or 'simple',
'context_lang' : 'en_US',
'menu_tips' : True
}
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
+3 -14
View File
@@ -132,31 +132,20 @@
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban default_group_by="department_id">
<field name="color"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value || record.name.raw_value)"/>
<div t-att-class="color + (record.color.raw_value == 1 ? ' oe_kanban_color_alert' : '')">
<div class="oe_kanban_box oe_kanban_color_border">
<div class="oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle">
<table class="oe_kanban_table">
<tr>
<td class="oe_kanban_title1" align="left" valign="middle"><field name="name"/></td>
<td valign="top" width="22">
<img t-att-src="kanban_gravatar(record.user_id.value, 22)" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
<div class="oe_kanban_title1"><field name="name"/></div>
</div>
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger oe_kanban_color_border">
<table class="oe_kanban_table">
<tr>
<td valign="top" width="22">
<t t-if="record.photo.raw_value">
<img t-att-src="'data:image/png;base64,'+record.photo.raw_value" width='48' height='48'/>
</t>
<t t-if="!record.photo.raw_value">
<img src="/web/static/src/img/employee.png"/>
</t>
<img t-att-src="kanban_image('hr.employee', 'photo', record.id.value)" width="48"/>
</td>
<td>
<div class="oe_kanban_title2"><field name="job_id"/></div>
+1 -1
View File
@@ -21,7 +21,7 @@
{
'name': 'Attendances',
'name': 'Attendance',
'version': '1.1',
'category': 'Human Resources',
'complexity': "easy",
+5 -5
View File
@@ -50,7 +50,7 @@
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Hr Attendance Search">
<filter icon="terp-stock_align_left_24" string="My Attendances" domain="[('employee_id.user_id.id', '=', uid)]" />
<filter icon="terp-stock_align_left_24" string="My Attendance" domain="[('employee_id.user_id.id', '=', uid)]" />
<separator orientation="vertical"/>
<filter icon="terp-go-today" string="Today" name="today" domain="[('name','&gt;=',current_date),('name','&lt;=',current_date)]" />
<separator orientation="vertical"/>
@@ -68,7 +68,7 @@
</record>
<record id="open_view_attendance" model="ir.actions.act_window">
<field name="name">Attendances</field>
<field name="name">Attendance</field>
<field name="res_model">hr.attendance</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
@@ -79,7 +79,7 @@
<menuitem id="menu_hr_time_tracking" name="Time Tracking" parent="hr.menu_hr_root" sequence="3" groups="base.group_user,base.group_hr_user,base.group_hr_manager"/>
<menuitem id="menu_hr_attendance" name="Attendances" parent="hr.menu_hr_root" sequence="4" groups="base.group_user,base.group_hr_user,base.group_hr_manager"/>
<menuitem id="menu_hr_attendance" name="Attendance" parent="hr.menu_hr_root" sequence="4" groups="base.group_user,base.group_hr_user,base.group_hr_manager"/>
<menuitem action="open_view_attendance" id="menu_open_view_attendance" parent="menu_hr_attendance" sequence="20"/>
@@ -116,8 +116,8 @@
<field name="view_id" ref="view_attendance_reason"/>
</record>
<menuitem
sequence="2" id="hr.menu_open_view_attendance_reason_new_config" parent="hr.menu_hr_configuration" name="Attendances"
<menuitem
sequence="2" id="hr.menu_open_view_attendance_reason_new_config" parent="hr.menu_hr_configuration" name="Attendance"
groups="base.group_extended"/>
<menuitem action="open_view_attendance_reason" id="menu_open_view_attendance_reason" parent="hr.menu_open_view_attendance_reason_new_config"/>
+584
View File
@@ -0,0 +1,584 @@
# Spanish (Chile) translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-01-11 11:15+0000\n"
"PO-Revision-Date: 2011-10-25 14:27+0000\n"
"Last-Translator: Juano <Unknown>\n"
"Language-Team: Spanish (Chile) <es_CL@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: 2011-10-26 05:13+0000\n"
"X-Generator: Launchpad (build 14189)\n"
#. module: hr_attendance
#: model:ir.ui.menu,name:hr_attendance.menu_hr_time_tracking
msgid "Time Tracking"
msgstr "Control de Tiempo"
#. module: hr_attendance
#: view:hr.attendance:0
msgid "Group By..."
msgstr "Agrupar por..."
#. module: hr_attendance
#: view:hr.attendance:0
msgid "Today"
msgstr "Hoy"
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "March"
msgstr "Marzo"
#. module: hr_attendance
#: view:hr.sign.in.out.ask:0
msgid ""
"You did not sign out the last time. Please enter the date and time you "
"signed out."
msgstr ""
"No registró la salida la última vez. Por favor, introduzca la fecha y hora "
"de la salida."
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid "Total period:"
msgstr "Total periodo:"
#. module: hr_attendance
#: field:hr.action.reason,name:0
msgid "Reason"
msgstr "Motivo"
#. module: hr_attendance
#: view:hr.attendance.error:0
msgid "Print Attendance Report Error"
msgstr "Imprimir informe errores asistenciales"
#. module: hr_attendance
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:156
#, python-format
msgid "The sign-out date must be in the past"
msgstr ""
"La fecha del registro de salida debe ser en el pasado o en tiempo pasado."
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid "Date Signed"
msgstr "Fecha registro"
#. module: hr_attendance
#: model:ir.actions.act_window,help:hr_attendance.open_view_attendance
msgid ""
"The Time Tracking functionality aims to manage employee attendances from "
"Sign in/Sign out actions. You can also link this feature to an attendance "
"device using OpenERP's web service features."
msgstr ""
#. module: hr_attendance
#: view:hr.action.reason:0
msgid "Attendance reasons"
msgstr ""
#. module: hr_attendance
#: view:hr.attendance:0
#: field:hr.attendance,day:0
msgid "Day"
msgstr ""
#. module: hr_attendance
#: selection:hr.employee,state:0
msgid "Present"
msgstr "Presente"
#. module: hr_attendance
#: model:ir.model,name:hr_attendance.model_hr_sign_in_out_ask
msgid "Ask for Sign In Out"
msgstr "Preguntar para registrar entrada / salida"
#. module: hr_attendance
#: field:hr.attendance,action_desc:0
#: model:ir.model,name:hr_attendance.model_hr_action_reason
msgid "Action Reason"
msgstr "Razón de la acción"
#. module: hr_attendance
#: view:hr.sign.in.out:0
msgid "Ok"
msgstr "Ok"
#. module: hr_attendance
#: view:hr.action.reason:0
msgid "Define attendance reason"
msgstr "Defina el motivo de la ausencia"
#. module: hr_attendance
#: constraint:hr.employee:0
msgid ""
"Error ! You cannot select a department for which the employee is the manager."
msgstr ""
"¡Error! No puede seleccionar un departamento para el cual el empleado sea el "
"responsable o director."
#. module: hr_attendance
#: model:ir.actions.act_window,name:hr_attendance.action_hr_attendance_month
msgid "Attendances By Month"
msgstr "Asistencia por mes"
#. module: hr_attendance
#: field:hr.sign.in.out,name:0
#: field:hr.sign.in.out.ask,name:0
msgid "Employees name"
msgstr "Nombre empleados"
#. module: hr_attendance
#: model:ir.actions.act_window,name:hr_attendance.open_view_attendance_reason
#: model:ir.ui.menu,name:hr_attendance.menu_open_view_attendance_reason
msgid "Attendance Reasons"
msgstr "Causas de asistencia/ausencia"
#. module: hr_attendance
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:156
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:162
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:169
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:174
#, python-format
msgid "UserError"
msgstr "Error del usuario"
#. module: hr_attendance
#: field:hr.attendance.error,end_date:0
#: field:hr.attendance.week,end_date:0
msgid "Ending Date"
msgstr "Fecha final"
#. module: hr_attendance
#: view:hr.attendance:0
msgid "Employee attendance"
msgstr "Asistencia del empleado"
#. module: hr_attendance
#: code:addons/hr_attendance/hr_attendance.py:136
#, python-format
msgid "Warning"
msgstr "Alerta"
#. module: hr_attendance
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:169
#, python-format
msgid "The Sign-in date must be in the past"
msgstr "La fecha del registro de entrada debe ser anterior o en el pasado"
#. module: hr_attendance
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:162
#, python-format
msgid "A sign-in must be right after a sign-out !"
msgstr "¡Un registro de entrada debe estar después de un registro de salida!"
#. module: hr_attendance
#: field:hr.employee,state:0
#: model:ir.model,name:hr_attendance.model_hr_attendance
msgid "Attendance"
msgstr "Asistencia"
#. module: hr_attendance
#: field:hr.attendance.error,max_delay:0
msgid "Max. Delay (Min)"
msgstr "Máx. retraso (minutos)"
#. module: hr_attendance
#: view:hr.attendance.error:0
#: view:hr.attendance.month:0
msgid "Print"
msgstr "Imprimir"
#. module: hr_attendance
#: view:hr.attendance:0
msgid "Hr Attendance Search"
msgstr "Buscar Asistencia RH"
#. module: hr_attendance
#: model:ir.module.module,description:hr_attendance.module_meta_information
msgid ""
"\n"
" This module aims to manage employee's attendances.\n"
" Keeps account of the attendances of the employees on the basis of the\n"
" actions(Sign in/Sign out) performed by them.\n"
" "
msgstr ""
"\n"
" Este módulo sirve para gestionar la asistencia de los empleados.\n"
" Controla la asistencia de los empleados mediante las acciones\n"
" (Registrar entrada/Registrar salida) realizadas por ellos.\n"
" "
#. module: hr_attendance
#: constraint:hr.attendance:0
msgid "Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)"
msgstr ""
"Error: Registro de entrada (resp. Registro de salida) debe seguir al "
"Registro de salida (resp. Registro de entrada)"
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "July"
msgstr "julio"
#. module: hr_attendance
#: model:ir.actions.act_window,name:hr_attendance.action_hr_attendance_error
#: model:ir.actions.report.xml,name:hr_attendance.attendance_error_report
msgid "Attendance Error Report"
msgstr "Reporte/Informe errores de asistencia"
#. module: hr_attendance
#: field:hr.attendance.error,init_date:0
#: field:hr.attendance.week,init_date:0
msgid "Starting Date"
msgstr "Fecha inicial"
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid "Min Delay"
msgstr "Mín. retraso"
#. module: hr_attendance
#: selection:hr.attendance,action:0
#: view:hr.employee:0
msgid "Sign In"
msgstr "Entrar"
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid "Operation"
msgstr "Operación"
#. module: hr_attendance
#: code:addons/hr_attendance/wizard/hr_attendance_error.py:49
#, python-format
msgid "No Data Available"
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "September"
msgstr "septiembre"
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "December"
msgstr "diciembre"
#. module: hr_attendance
#: field:hr.attendance.month,month:0
msgid "Month"
msgstr "Mes"
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid ""
"(*) A negative delay means that the employee worked more than encoded."
msgstr ""
"(*) Un retraso negativo significa que el empleado trabajó más horas de las "
"codificadas."
#. module: hr_attendance
#: help:hr.attendance,action_desc:0
msgid ""
"Specifies the reason for Signing In/Signing Out in case of extra hours."
msgstr "Especifique la razón de entrada y salida en el caso de horas extras."
#. module: hr_attendance
#: model:ir.model,name:hr_attendance.model_hr_attendance_month
msgid "Print Monthly Attendance Report"
msgstr "Imprimir informe mensual de asistencias"
#. module: hr_attendance
#: model:ir.model,name:hr_attendance.model_hr_sign_in_out
msgid "Sign In Sign Out"
msgstr "Entrada Salida"
#. module: hr_attendance
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:103
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:125
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:141
#: view:hr.sign.in.out:0
#: model:ir.actions.act_window,name:hr_attendance.action_hr_attendance_sigh_in_out
#: model:ir.ui.menu,name:hr_attendance.menu_hr_attendance_sigh_in_out
#, python-format
msgid "Sign in / Sign out"
msgstr "Registrar entrada/salida"
#. module: hr_attendance
#: view:hr.sign.in.out.ask:0
msgid "hr.sign.out.ask"
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "August"
msgstr "agosto"
#. module: hr_attendance
#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:174
#, python-format
msgid "A sign-out must be right after a sign-in !"
msgstr "¡Un registro de salida debe estar después de un registro de entrada!"
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "June"
msgstr "junio"
#. module: hr_attendance
#: model:ir.model,name:hr_attendance.model_hr_attendance_error
msgid "Print Error Attendance Report"
msgstr "Error en la impresión del informe de asistencia"
#. module: hr_attendance
#: model:ir.module.module,shortdesc:hr_attendance.module_meta_information
msgid "Attendances Of Employees"
msgstr ""
#. module: hr_attendance
#: field:hr.attendance,name:0
msgid "Date"
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "November"
msgstr ""
#. module: hr_attendance
#: constraint:hr.employee:0
msgid "Error ! You cannot create recursive Hierarchy of Employees."
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "October"
msgstr ""
#. module: hr_attendance
#: view:hr.attendance:0
msgid "My Attendances"
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "January"
msgstr ""
#. module: hr_attendance
#: selection:hr.action.reason,action_type:0
#: view:hr.sign.in.out:0
#: view:hr.sign.in.out.ask:0
msgid "Sign in"
msgstr ""
#. module: hr_attendance
#: view:hr.attendance.error:0
msgid "Analysis Information"
msgstr ""
#. module: hr_attendance
#: view:hr.sign.in.out:0
msgid "Sign-Out Entry must follow Sign-In."
msgstr ""
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid "Attendance Errors"
msgstr ""
#. module: hr_attendance
#: field:hr.attendance,action:0
#: selection:hr.attendance,action:0
msgid "Action"
msgstr ""
#. module: hr_attendance
#: view:hr.sign.in.out:0
msgid ""
"If you need your staff to sign in when they arrive at work and sign out "
"again at the end of the day, OpenERP allows you to manage this with this "
"tool. If each employee has been linked to a system user, then they can "
"encode their time with this action button."
msgstr ""
#. module: hr_attendance
#: field:hr.sign.in.out,emp_id:0
msgid "Employee ID"
msgstr ""
#. module: hr_attendance
#: model:ir.model,name:hr_attendance.model_hr_attendance_week
msgid "Print Week Attendance Report"
msgstr ""
#. module: hr_attendance
#: field:hr.sign.in.out.ask,emp_id:0
msgid "Empoyee ID"
msgstr ""
#. module: hr_attendance
#: view:hr.attendance.error:0
#: view:hr.attendance.month:0
#: view:hr.sign.in.out:0
#: view:hr.sign.in.out.ask:0
msgid "Cancel"
msgstr ""
#. module: hr_attendance
#: help:hr.action.reason,name:0
msgid "Specifies the reason for Signing In/Signing Out."
msgstr ""
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid ""
"(*) A positive delay means that the employee worked less than recorded."
msgstr ""
#. module: hr_attendance
#: view:hr.attendance.month:0
msgid "Print Attendance Report Monthly"
msgstr ""
#. module: hr_attendance
#: selection:hr.action.reason,action_type:0
#: view:hr.sign.in.out:0
#: view:hr.sign.in.out.ask:0
msgid "Sign out"
msgstr ""
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid "Delay"
msgstr ""
#. module: hr_attendance
#: view:hr.attendance:0
#: model:ir.model,name:hr_attendance.model_hr_employee
msgid "Employee"
msgstr ""
#. module: hr_attendance
#: code:addons/hr_attendance/hr_attendance.py:136
#, python-format
msgid ""
"You tried to %s with a date anterior to another event !\n"
"Try to contact the administrator to correct attendances."
msgstr ""
#. module: hr_attendance
#: view:hr.sign.in.out.ask:0
#: field:hr.sign.in.out.ask,last_time:0
msgid "Your last sign out"
msgstr ""
#. module: hr_attendance
#: report:report.hr.timesheet.attendance.error:0
msgid "Date Recorded"
msgstr ""
#. module: hr_attendance
#: model:ir.actions.act_window,name:hr_attendance.open_view_attendance
#: model:ir.ui.menu,name:hr_attendance.menu_hr_attendance
#: model:ir.ui.menu,name:hr_attendance.menu_open_view_attendance
msgid "Attendances"
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "May"
msgstr ""
#. module: hr_attendance
#: view:hr.sign.in.out.ask:0
msgid "Your last sign in"
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance,action:0
#: view:hr.employee:0
msgid "Sign Out"
msgstr ""
#. module: hr_attendance
#: model:ir.actions.act_window,help:hr_attendance.action_hr_attendance_sigh_in_out
msgid ""
"Sign in / Sign out. In some companies, staff have to sign in when they "
"arrive at work and sign out again at the end of the day. If each employee "
"has been linked to a system user, then they can encode their time with this "
"action button."
msgstr ""
#. module: hr_attendance
#: field:hr.attendance,employee_id:0
msgid "Employee's Name"
msgstr ""
#. module: hr_attendance
#: selection:hr.employee,state:0
msgid "Absent"
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "February"
msgstr ""
#. module: hr_attendance
#: field:hr.action.reason,action_type:0
msgid "Action's type"
msgstr ""
#. module: hr_attendance
#: view:hr.attendance:0
msgid "Employee attendances"
msgstr ""
#. module: hr_attendance
#: field:hr.sign.in.out,state:0
msgid "Current state"
msgstr ""
#. module: hr_attendance
#: selection:hr.attendance.month,month:0
msgid "April"
msgstr ""
#. module: hr_attendance
#: view:hr.attendance.error:0
msgid "Bellow this delay, the error is considered to be voluntary"
msgstr ""
#. module: hr_attendance
#: code:addons/hr_attendance/wizard/hr_attendance_error.py:49
#, python-format
msgid "No records found for your selection!"
msgstr ""
#. module: hr_attendance
#: view:hr.sign.in.out.ask:0
msgid ""
"You did not sign in the last time. Please enter the date and time you signed "
"in."
msgstr ""
#. module: hr_attendance
#: field:hr.attendance.month,year:0
msgid "Year"
msgstr ""
#. module: hr_attendance
#: view:hr.sign.in.out.ask:0
msgid "hr.sign.in.out.ask"
msgstr ""
+1 -1
View File
@@ -21,7 +21,7 @@
{
'name': "Employee's Contracts",
'name': "Employee Contracts",
'version': '1.0',
'category': 'Human Resources',
'complexity': "easy",
+1 -1
View File
@@ -21,7 +21,7 @@
{
"name": "Leaves Management",
"name": "Leave Management",
"version": "1.5",
"author": ['OpenERP SA', 'Axelor'],
"category": "Human Resources",
+14 -14
View File
@@ -1,37 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="BASIC" model="hr.salary.rule.category">
<field name="name">Basic</field>
<field name="code">BASIC</field>
</record>
<record id="ALW" model="hr.salary.rule.category">
<field name="name">Allowance</field>
<field name="code">ALW</field>
</record>
<record id="DED" model="hr.salary.rule.category">
<field name="name">Deduction</field>
<field name="code">DED</field>
</record>
<record id="GROSS" model="hr.salary.rule.category">
<field name="name">Gross</field>
<field name="code">GROSS</field>
</record>
<record id="DED" model="hr.salary.rule.category">
<field name="name">Deduction</field>
<field name="code">DED</field>
</record>
<record id="NET" model="hr.salary.rule.category">
<field name="name">Net</field>
<field name="code">NET</field>
</record>
<record id="COMP" model="hr.salary.rule.category">
<field name="name">Company Contribution</field>
<field name="code">COMP</field>
</record>
<record id="hr_rule_basic" model="hr.salary.rule">
<field name="name">Basic</field>
<field name="sequence" eval="1"/>
@@ -71,13 +71,13 @@
<field eval="[(6, 0, [ref('hr_rule_basic'), ref('hr_rule_taxable'),ref('hr_rule_net')])]" name="rule_ids"/>
<field name="company_id" ref="base.main_company"/>
</record>
<!-- Decimal Precision -->
<record forcecreate="True" id="decimal_payroll" model="decimal.precision">
<field name="name">Payroll</field>
<field name="digits">2</field>
</record>
</data>
</openerp>
+9 -12
View File
@@ -286,24 +286,21 @@
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id">
<field name="color"/>
<field name="state"/>
<field name="priority"/>
<field name="survey"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value || record.state.raw_value)"/>
<div t-att-class="color + (record.priority.raw_value == 1 ? ' oe_kanban_color_alert' : '')">
<div class="oe_kanban_box oe_kanban_color_border">
<div class="oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle">
<table class="oe_kanban_table">
<tr>
<td class="oe_kanban_title1" align="left" valign="middle">
<a t-if="record.priority.raw_value == 1" icon="star-on" type="object" name="set_normal_priority" />
<a t-if="record.priority.raw_value != 1" icon="star-off" type="object" name="set_high_priority" style="opacity:0.6; filter:alpha(opacity=60);"/>
<field name="partner_name"/>
</td>
<td valign="top" width="22">
<img t-att-src="kanban_gravatar(record.user_id.value, 22)" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
<div class="oe_kanban_title1">
<a t-if="record.priority.raw_value == 1" icon="star-on" type="object" name="set_normal_priority" />
<a t-if="record.priority.raw_value != 1" icon="star-off" type="object" name="set_high_priority" style="opacity:0.6; filter:alpha(opacity=60);"/>
<field name="partner_name"/>
</div>
</div>
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger oe_kanban_color_border">
<table class="oe_kanban_table">
+23 -18
View File
@@ -665,7 +665,7 @@ class mrp_repair_line(osv.osv, ProductChangeMixin):
'product_uom_qty': lambda *a: 1,
}
def onchange_operation_type(self, cr, uid, ids, type, guarantee_limit):
def onchange_operation_type(self, cr, uid, ids, type, guarantee_limit, company_id=False, context=None):
""" On change of operation type it sets source location, destination location
and to invoice field.
@param product: Changed operation type.
@@ -676,27 +676,32 @@ class mrp_repair_line(osv.osv, ProductChangeMixin):
return {'value': {
'location_id': False,
'location_dest_id': False
}
}
}}
warehouse_obj = self.pool.get('stock.warehouse')
location_id = self.pool.get('stock.location').search(cr, uid, [('usage','=','production')], context=context)
location_id = location_id and location_id[0] or False
if type == 'add':
# TOCHECK: Find stock location for user's company warehouse or
# repair order's company's warehouse (company_id field is added in fix of lp:831583)
args = company_id and [('company_id', '=', company_id)] or []
warehouse_ids = warehouse_obj.search(cr, uid, args, context=context)
stock_id = False
if warehouse_ids:
stock_id = warehouse_obj.browse(cr, uid, warehouse_ids[0], context=context).lot_stock_id.id
to_invoice = (guarantee_limit and datetime.strptime(guarantee_limit, '%Y-%m-%d') < datetime.now())
product_id = self.pool.get('stock.location').search(cr, uid, [('name','=','Production')])[0]
if type != 'add':
return {'value': {
'to_invoice': False,
'location_id': product_id,
'location_dest_id': False
}
}
'to_invoice': to_invoice,
'location_id': stock_id,
'location_dest_id': location_id
}}
stock_id = self.pool.get('stock.location').search(cr, uid, [('name','=','Stock')])[0]
to_invoice = (guarantee_limit and
datetime.strptime(guarantee_limit, '%Y-%m-%d') < datetime.now())
return {'value': {
'to_invoice': to_invoice,
'location_id': stock_id,
'location_dest_id': product_id
}
}
'to_invoice': False,
'location_id': location_id,
'location_dest_id': False
}}
mrp_repair_line()
+2 -2
View File
@@ -59,7 +59,7 @@
<field name="location_id"/>
<field name="location_dest_id"/>
<newline/>
<field name="type" on_change="onchange_operation_type(type,parent.guarantee_limit)"/>
<field name="type" on_change="onchange_operation_type(type,parent.guarantee_limit,parent.company_id,context)"/>
<group colspan="2">
<field name="to_invoice"/>
<field name="invoiced"/>
@@ -77,7 +77,7 @@
</notebook>
</form>
<tree string="Operations" editable="bottom">
<field name="type" on_change="onchange_operation_type(type,parent.guarantee_limit)"/>
<field name="type" on_change="onchange_operation_type(type,parent.guarantee_limit,parent.company_id,context)"/>
<field name="product_id" on_change="product_id_change(parent.pricelist_id,product_id,product_uom,product_uom_qty, parent.partner_id)"/>
<field name='name'/>
<field name="prodlot_id" />
+1 -1
View File
@@ -61,7 +61,7 @@
On change of operation type it sets source location, destination location and to invoice field
-
!python {model: mrp.repair.line}: |
self.onchange_operation_type(cr, uid, [ref('mrp_repair_rma0')], type, False)
self.onchange_operation_type(cr, uid, [ref('mrp_repair_rma0')], type, False, ref('base.main_company'), {})
-
On change of partner sets the values of partner address, partner invoice address and pricelist.
-
-4
View File
@@ -40,10 +40,6 @@ class share_wizard_portal(osv.osv_memory):
_columns = {
'user_ids': fields.many2many('res.users', 'share_wizard_res_user_rel', 'share_id', 'user_id', 'Existing users', domain=[('share', '=', True)]),
'group_ids': fields.many2many('res.groups', 'share_wizard_res_group_rel', 'share_id', 'group_id', 'Existing groups', domain=[('share', '=', False)]),
# no clean way to extend selection yet, copy the field.
'user_type': fields.selection(_user_type_selection,'Users to share with',
help="Select the type of user(s) you would like to share data with."),
}
def is_portal_manager(self, cr, uid, context=None):
+1 -5
View File
@@ -8,11 +8,7 @@
<field name="type">form</field>
<field name="inherit_id" ref="share.share_step1_form"/>
<field name="arch" type="xml">
<xpath expr="/form/group[@name='emails_group']" position="before">
<group name="groups_group" colspan="4" col="4" groups="portal.group_portal_manager">
<separator colspan="4" string="Who do you want to share with?"/>
<field name="user_type" />
</group>
<xpath expr="/form/group[@name='emails_group']" position="after">
<group name="user_group" colspan="4" attrs="{'invisible':[('user_type','!=','existing')]}">
<field colspan="4" nolabel="1" name="user_ids" mode="tree">
<tree string="Existing users">
+14 -20
View File
@@ -202,39 +202,33 @@
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban default_group_by="type">
<field name="color"/>
<field name="type"/>
<field name="product_image"/>
<field name="list_price"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value || record.type.raw_value)"/>
<div t-att-class="color + (record.color.raw_value == 1 ? ' oe_kanban_color_alert' : '')">
<div class="oe_kanban_box oe_kanban_color_border">
<table class="oe_kanban_table oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle">
<tr>
<td class="oe_kanban_title1" align="left" valign="middle">
<t t-if="record.default_code.raw_value">[<field name="default_code"/>]</t>
<field name="name"/>
</td>
<td align="right" valign="top" width="22">
<img t-att-src="kanban_gravatar(record.product_manager.raw_value, 22)" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
<div class="oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle oe_kanban_title1">
<field name="name"/>
</div>
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger">
<table class="oe_kanban_table">
<tr>
<td valign="top" width="22">
<t t-if="record.product_image.raw_value">
<img t-att-src="'data:image/png;base64,'+record.product_image.raw_value" width='48' height='48'/>
</t>
<t t-if="!record.product_image.raw_value">
<img src="/web/static/src/img/product_default.png"/>
</t>
<td valign="top" width="48">
<img t-att-src="kanban_image('product.product', 'product_image', record.id.value)" width="48"/>
</td>
<td>
<div class="oe_kanban_title2">
<t t-if="record.type.raw_value=='service'">No Stock</t>
<t t-if="record.type.raw_value!='service'">
<div><field name="qty_available"/> On Hand , <field name="virtual_available"/> Available</div>
<div><t t-if="record.list_price.raw_value!=0">Public Price : <field name="lst_price"/> ,</t> Cost : <field name="standard_price"/></div>
<div>
<t t-if="record.list_price.raw_value!=0">Public Price : <field name="lst_price"/> ,</t>
Cost : <field name="standard_price"/>
</div>
</t>
</div>
</td>
@@ -255,7 +249,7 @@
</kanban>
</field>
</record>
<record id="product_normal_action" model="ir.actions.act_window">
<field name="name">Products</field>
<field name="type">ir.actions.act_window</field>
+2 -1
View File
@@ -471,6 +471,7 @@ class task(osv.osv):
'company_id': fields.many2one('res.company', 'Company'),
'id': fields.integer('ID', readonly=True),
'color': fields.integer('Color Index'),
'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
}
_defaults = {
@@ -484,7 +485,7 @@ class task(osv.osv):
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c)
}
_order = "sequence,priority, date_start, name, id"
_order = "priority, sequence, date_start, name, id"
def set_priority(self, cr, uid, ids, priority):
"""Set task priority
+22 -29
View File
@@ -175,7 +175,7 @@
<field name="context">{'search_default_Current':1}</field>
<field name="help">A project contains a set of tasks or issues that will be performed by your resources assigned to it. A project can be hierarchically structured, as a child of a Parent Project. This allows you to design large project structures with different phases spread over the project duration cycle. Each user can set his default project in his own preferences to automatically filter the tasks or issues he usually works on. If you choose to invoice the time spent on a project task, you can find project tasks to be invoiced in the billing section.</field>
</record>
<menuitem action="open_view_project_all" id="menu_open_view_project_all" parent="menu_project_management" sequence="1"/>
<menuitem action="open_view_project_all" id="menu_open_view_project_all" parent="menu_definitions" sequence="1"/>
<record id="open_view_template_project" model="ir.actions.act_window">
<field name="name">Templates of Projects</field>
@@ -323,12 +323,14 @@
<kanban default_group_by="type_id" >
<field name="color"/>
<field name="priority"/>
<field name="state"/>
<field name="type_id"/>
<field name="user_id"/>
<field name="user_email"/>
<field name="description"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value || record.type_id.raw_value)"/>
<div t-att-class="color + (record.priority.raw_value == 1 ? ' oe_kanban_color_alert' : '')">
<t t-set="pad_url">http://pad.openerp.com/<t t-raw="_.underscored(_.trim(record.name.raw_value))"/></t>
<div t-att-class="kanban_color(record.color.raw_value) + (record.state.raw_value === 'pending' ? ' oe_kanban_color_alert' : '')">
<div class="oe_kanban_box oe_kanban_color_border">
<table class="oe_kanban_table oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle">
<tr>
@@ -338,40 +340,31 @@
<field name="name"/>
</td>
<td valign="top" width="22">
<img t-att-src="kanban_gravatar(record.user_id.value, 22)" class="oe_kanban_gravatar"/>
<img t-att-src="kanban_gravatar(record.user_email.value, 22)" class="oe_kanban_gravatar" t-att-title="record.user_id.value"/>
</td>
</tr>
</table>
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger">
<div class="oe_kanban_right oe_kanban_small">
<field name="user_id"/>
<div class="oe_kanban_right">
<b>
<field name="project_id"/><br/>
<field name="state"/><t t-if="record.date_deadline.value"> - </t>
<i><field name="date_deadline"/></i>
</b>
</div>
<div class="oe_kanban_title2">
<field name="project_id"/>
</div>
<div class="oe_kanban_small">
<a target="_blank" t-att-href="'http://pad.openerp.com/'+record.name.raw_value.toLowerCase().replace(' ','-')">
http://pad.openerp.com/<t t-esc="record.name.raw_value.toLowerCase().replace(' ','-')"/>
</a>
</div>
<div class="oe_kanban_small">
<i><field name="date_deadline"/></i>
<div>
<t t-esc="kanban_text_ellipsis(record.description.value, 160)"/>
</div>
<br class="oe_kanban_clear"/>
</div>
<div class="oe_kanban_buttons_set oe_kanban_color_border oe_kanban_color_bglight oe_kanban_box_show_onclick">
<div class="oe_kanban_left">
<a string="Edit" icon="gtk-edit" type="edit"/>
<a string="Edit" icon="gtk-edit" type="edit"/>
<a string="Change Color" icon="color-picker" type="color" name="color"/>
<a string="" />
<a title="OpenERP Pad" icon="pad-openerp" target="_blank" t-att-href="'http://pad.openerp.com/'+record.name.value" style="text-decoration: none;" >
<img src="/web/static/src/img/icons/pad-openerp.png" border="0" width="16" height="16"/>
</a>
<a name="%(action_project_task_delegate)d" string="Delegate" states="pending,open,draft" type="action" icon="gtk-sort-descending" />
</div>
<div class="oe_kanban_right">
<a name="do_cancel" string="Mark Lost" states="draft,open,pending" type="object" icon="lead-stage-lost"/>
<a name="do_pending" string="Pending" states="open" type="object" icon="gtk-media-pause"/>
<a name="action_close" string="Mark Won" states="pending,open" type="object" icon="lead-stage-won"/>
<a name="do_pending" string="Set Pending" states="draft,open" type="object" icon="gtk-media-pause"/>
<a name="do_open" string="Open" states="pending" type="object" icon="gtk-media-play"/>
</div>
<br class="oe_kanban_clear"/>
</div>
@@ -467,8 +460,8 @@
<field name="arch" type="xml">
<search string="Task Edition">
<group>
<filter string="New" domain="[('state','=','draft')]" name="current" help="New Tasks" icon="terp-check"/>
<filter string="In Progress" domain="[('state','=','open')]" help="In Progress Tasks" icon="terp-camera_test"/>
<filter name="draft" string="New" domain="[('state','=','draft')]" help="New Tasks" icon="terp-check"/>
<filter name="open" string="In Progress" domain="[('state','=','open')]" help="In Progress Tasks" icon="terp-camera_test"/>
<filter string="Pending" domain="[('state','=','pending')]" context="{'show_delegated':False}" help="Pending Tasks" icon="terp-gtk-media-pause"/>
<separator orientation="vertical"/>
<filter name="edit"
@@ -511,7 +504,7 @@
<field name="view_mode">tree,form,calendar,gantt,graph,kanban</field>
<field eval="False" name="filter"/>
<field name="view_id" ref="view_task_tree2"/>
<field name="context">{"search_default_user_id":uid, "search_default_current": 1}</field>
<field name="context">{"search_default_user_id":uid, "search_default_draft": 1, "search_default_open":1 }</field>
<field name="search_view_id" ref="view_task_search_form"/>
<field name="help">A task represents a work that has to be done. Each user works in his own list of tasks where he can record his task work in hours. He can work and close the task itself or delegate it to another user. If you delegate a task to another user, you get a new task in pending state, which will be reopened when you have to review the work achieved. If you install the project_timesheet module, task work can be invoiced based on the project configuration. With the project_mrp module, sales orders can create tasks automatically when they are confirmed.</field>
</record>
Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

+2 -1
View File
@@ -226,7 +226,8 @@ class project_issue(crm.crm_case, osv.osv):
multi='compute_day', type="float", store=True),
'inactivity_days': fields.function(_compute_day, string='Days since last action', \
multi='compute_day', type="integer", help="Difference in days between last action and current date"),
'color': fields.integer('Color Index'),
'color': fields.integer('Color Index'),
'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
+10 -6
View File
@@ -261,15 +261,19 @@
</record>
<!-- Project Issue Karban View-->
<!-- Project Issue Karban View-->
<record model="ir.ui.view" id="project_issue_kanban_view">
<record model="ir.ui.view" id="project_issue_kanban_view">
<field name="name">Project Issue Kanban</field>
<field name="model">project.issue</field>
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban default_group_by="type_id">
<templates>
<field name="color"/>
<field name="state"/>
<field name="priority"/>
<field name="user_email"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value || record.state.raw_value)"/>
<div t-att-class="color + (record.priority.raw_value == 1 ? ' oe_kanban_color_alert' : '')">
@@ -283,7 +287,7 @@
<field name="name"/>
</td>
<td valign="top" width="22">
<img t-att-src="kanban_gravatar(record.user_id.value, 22)" class="oe_kanban_gravatar"/>
<img t-att-src="kanban_gravatar(record.user_email.value, 22)" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
@@ -315,11 +319,11 @@
<a string="Send New Email" name="%(mail.action_email_compose_message_wizard)d" icon="terp-mail-message-new" type="action"/>
<a string="Add Internal Note" name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action"/>
<a string="Escalate" name="case_escalate" icon="gtk-go-up" type="object"/>
<a string="Convert To Task" name="convert_issue_task" icon="gtk-index" type="object"/>
<a string="Convert To Task" name="convert_issue_task" icon="gtk-index" type="object"/>
</div>
<div class="oe_kanban_right">
<a name="case_open" string="Open" states="draft,pending" type="object" icon="lead-stage-won" />
<a name="case_pending" string="Pending" states="draft,open" type="object" icon="lead-stage-pending"/>
<a name="case_pending" string="Pending" states="draft,open" type="object" icon="lead-stage-pending"/>
<a name="case_close" string="Close" states="open,draft,pending" type="object" icon="lead-stage-lost"/>
</div>
<br class="oe_kanban_clear"/>
-2
View File
@@ -49,13 +49,11 @@ Features
'test/test_schedule_phases_case2.yml',
'test/project_schedule_consecutive_day.yml',
'test/project_schedule_without_wroking_hour.yml',
'test/schedule_phase_tasks.yml',
'test/phase_constraint.yml',
'test/test_schedule_tasks_case1.yml',
],
"update_xml": [
"security/ir.model.access.csv",
"wizard/project_schedule_tasks_view.xml",
"project_long_term_view.xml",
"project_long_term_workflow.xml",
"wizard/project_compute_phases_view.xml",
+202 -575
View File
@@ -22,7 +22,7 @@
from datetime import datetime
from tools.translate import _
from osv import fields, osv
from resource.faces import task as Task
from resource.faces import task as Task
from operator import itemgetter
class project_phase(osv.osv):
@@ -74,40 +74,43 @@ class project_phase(osv.osv):
return False
return True
def _check_constraint_start(self, cr, uid, ids, context=None):
phase = self.read(cr, uid, ids[0], ['date_start', 'constraint_date_start'], context=context)
if phase['date_start'] and phase['constraint_date_start'] and phase['date_start'] < phase['constraint_date_start']:
return False
return True
def _check_constraint_end(self, cr, uid, ids, context=None):
phase = self.read(cr, uid, ids[0], ['date_end', 'constraint_date_end'], context=context)
if phase['date_end'] and phase['constraint_date_end'] and phase['date_end'] > phase['constraint_date_end']:
return False
return True
def _get_default_uom_id(self, cr, uid):
model_data_obj = self.pool.get('ir.model.data')
model_data_id = model_data_obj._get_id(cr, uid, 'product', 'uom_hour')
return model_data_obj.read(cr, uid, [model_data_id], ['res_id'])[0]['res_id']
def _compute(self, cr, uid, ids, field_name, arg, context=None):
def _compute_progress(self, cr, uid, ids, field_name, arg, context=None):
res = {}
if not ids:
return res
for phase in self.browse(cr, uid, ids, context=context):
tot = 0.0
if phase.state=='done':
res[phase.id] = 100.0
continue
elif phase.state=="cancelled":
res[phase.id] = 0.0
continue
elif not phase.task_ids:
res[phase.id] = 0.0
continue
tot = done = 0.0
for task in phase.task_ids:
tot += task.planned_hours
res[phase.id] = tot
tot += task.total_hours
done += min(task.effective_hours, task.total_hours)
if not tot:
res[phase.id] = 0.0
else:
res[phase.id] = round(100.0 * done / tot, 2)
return res
_columns = {
'name': fields.char("Name", size=64, required=True),
'date_start': fields.date('Start Date', help="It's computed by the scheduler according the project date or the end date of the previous phase.", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'date_end': fields.date('End Date', help=" It's computed by the scheduler according to the start date and the duration.", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'constraint_date_start': fields.date('Minimum Start Date', help='force the phase to start after this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'constraint_date_end': fields.date('Deadline', help='force the phase to finish before this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'date_start': fields.datetime('Start Date', help="It's computed by the scheduler according the project date or the end date of the previous phase.", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'date_end': fields.datetime('End Date', help=" It's computed by the scheduler according to the start date and the duration.", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'constraint_date_start': fields.datetime('Minimum Start Date', help='force the phase to start after this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'constraint_date_end': fields.datetime('Deadline', help='force the phase to finish before this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'project_id': fields.many2one('project.project', 'Project', required=True),
'next_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'prv_phase_id', 'next_phase_id', 'Next Phases', states={'cancelled':[('readonly',True)]}),
'previous_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'next_phase_id', 'prv_phase_id', 'Previous Phases', states={'cancelled':[('readonly',True)]}),
@@ -115,74 +118,27 @@ class project_phase(osv.osv):
'duration': fields.float('Duration', required=True, help="By default in days", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'product_uom': fields.many2one('product.uom', 'Duration UoM', required=True, help="UoM (Unit of Measure) is the unit of measurement for Duration", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'task_ids': fields.one2many('project.task', 'phase_id', "Project Tasks", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'resource_ids': fields.one2many('project.resource.allocation', 'phase_id', "Project Resources",states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'responsible_id': fields.many2one('res.users', 'Responsible', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'user_force_ids': fields.many2many('res.users', string='Force Assigned Users'),
'user_ids': fields.one2many('project.user.allocation', 'phase_id', "Assigned Users",states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]},
help="The ressources on the project can be computed automatically by the scheduler"),
'state': fields.selection([('draft', 'New'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True,
help='If the phase is created the state \'Draft\'.\n If the phase is started, the state becomes \'In Progress\'.\n If review is needed the phase is in \'Pending\' state.\
\n If the phase is over, the states is set to \'Done\'.'),
'total_hours': fields.function(_compute, string='Total Hours'),
'progress': fields.function(_compute_progress, string='Progress', help="Computed based on related tasks"),
}
_defaults = {
'responsible_id': lambda obj,cr,uid,context: uid,
'state': 'draft',
'sequence': 10,
'product_uom': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('Day'))], context=c)[0]
}
_order = "project_id, date_start, sequence, name"
_order = "project_id, date_start, sequence"
_constraints = [
(_check_recursion,'Loops in phases not allowed',['next_phase_ids', 'previous_phase_ids']),
(_check_dates, 'Phase start-date must be lower than phase end-date.', ['date_start', 'date_end']),
]
def onchange_project(self, cr, uid, ids, project, context=None):
result = {}
result['date_start'] = False
project_obj = self.pool.get('project.project')
if project:
project_id = project_obj.browse(cr, uid, project, context=context)
result['date_start'] = project_id.date_start
return {'value': result}
def _check_date_start(self, cr, uid, phase, date_end, context=None):
"""
Check And Compute date_end of phase if change in date_start < older time.
"""
uom_obj = self.pool.get('product.uom')
resource_obj = self.pool.get('resource.resource')
cal_obj = self.pool.get('resource.calendar')
calendar_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
if resource_id:
res = resource_obj.read(cr, uid, resource_id, ['calendar_id'], context=context)[0]
cal_id = res.get('calendar_id', False) and res.get('calendar_id')[0] or False
if cal_id:
calendar_id = cal_id
default_uom_id = self._get_default_uom_id(cr, uid)
avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
work_times = cal_obj.interval_min_get(cr, uid, calendar_id, date_end, avg_hours or 0.0, resource_id and resource_id[0] or False)
dt_start = work_times[0][0].strftime('%Y-%m-%d')
self.write(cr, uid, [phase.id], {'date_start': dt_start, 'date_end': date_end.strftime('%Y-%m-%d')}, context=context)
def _check_date_end(self, cr, uid, phase, date_start, context=None):
"""
Check And Compute date_end of phase if change in date_end > older time.
"""
uom_obj = self.pool.get('product.uom')
resource_obj = self.pool.get('resource.resource')
cal_obj = self.pool.get('resource.calendar')
calendar_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)], context=context)
if resource_id:
res = resource_obj.read(cr, uid, resource_id, ['calendar_id'], context=context)[0]
cal_id = res.get('calendar_id', False) and res.get('calendar_id')[0] or False
if cal_id:
calendar_id = cal_id
default_uom_id = self._get_default_uom_id(cr, uid)
avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
work_times = cal_obj.interval_get(cr, uid, calendar_id, date_start, avg_hours or 0.0, resource_id and resource_id[0] or False)
dt_end = work_times[-1][1].strftime('%Y-%m-%d')
self.write(cr, uid, [phase.id], {'date_start': date_start.strftime('%Y-%m-%d'), 'date_end': dt_end}, context=context)
return {}
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
@@ -211,208 +167,57 @@ class project_phase(osv.osv):
self.write(cr, uid, ids, {'state': 'done'})
return True
def generate_phase(self, cr, uid, ids, f, parent=False, context=None):
if context is None:
context = {}
phase_ids = []
data_pool = self.pool.get('ir.model.data')
uom_pool = self.pool.get('product.uom')
task_pool = self.pool.get('project.task')
data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
for phase in self.browse(cr, uid, ids, context=context):
avg_days = uom_pool._compute_qty(cr, uid, phase.product_uom.id, phase.duration, day_uom_id)
duration = str(avg_days) + 'd'
# Create a new project for each phase
str_resource = ('%s & '*len(phase.resource_ids))[:-2]
str_vals = str_resource % tuple(map(lambda x: 'Resource_%s'%x.resource_id.id, phase.resource_ids))
def generate_phase(self, cr, uid, phases, context=None):
context = context or {}
result = ""
# Phases Defination for the Project
s = '''
task_pool = self.pool.get('project.task')
for phase in phases:
if phase.state in ('done','cancelled'):
continue
duration_uom = {
'days': 'd', 'day': 'd', 'd':'d',
'months': 'm', 'month':'month', 'm':'m',
'weeks': 'w', 'week': 'w', 'w':'w',
'hours': 'H', 'hour': 'H', 'h':'H',
}.get(phase.product_uom.name.lower(), "h")
duration = str(phase.duration) + duration_uom
result += '''
def Phase_%s():
title = \"%s\"
effort = \'%s\'
resource = %s
'''%(phase.id, phase.name, duration, str_vals or False)
# Recalculate date_start and date_end
# according to constraints on date start and date end on phase
start_date = ''
end_date = ''
effort = \"%s\"''' % (phase.id, duration)
start = []
if phase.constraint_date_start:
start_date = datetime.strptime(phase.constraint_date_start, '%Y-%m-%d')
s += '''
start = \"%s\"
'''%(datetime.strftime(start_date, "%Y-%m-%d"))
else:
if parent:
start = 'up.Phase_%s.end' % (parent.id)
s += '''
start = %s
'''%(start)
else:
start = phase.project_id.date_start or phase.date_start
s += '''
start = \"%s\"
'''%(start)
if phase.constraint_date_end :
end_date= datetime.strptime(phase.constraint_date_end, '%Y-%m-%d')
s += '''
end = \"%s\"
'''%(datetime.strftime(end_date, "%Y-%m-%d"))
start.append('datetime.datetime.strptime("'+str(phase.constraint_date_start)+'", "%Y-%m-%d %H:%M:%S")')
for previous_phase in phase.previous_phase_ids:
start.append("up.Phase_%s.end" % (previous_phase.id,))
if start:
result += '''
start = max(%s)
''' % (','.join(start))
if phase.user_force_ids:
result += '''
resource = %s
''' % '|'.join(map(lambda x: 'User_'+str(x.id), phase.user_force_ids))
#start = datetime.strftime((datetime.strptime(start, "%Y-%m-%d")), "%Y-%m-%d")
result += task_pool._generate_task(cr, uid, phase.task_ids, ident=8, context=context)
result += "\n"
phase_ids.append(phase.id)
parent = False
task_ids = []
todo_task_ids = task_pool.search(cr, uid, [('id', 'in', map(lambda x : x.id, phase.task_ids)),
('state', 'in', ['draft', 'open', 'pending'])
], order='sequence')
if todo_task_ids :
for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
s += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=False, context=context)
if not parent:
parent = task
task_ids.append(task.id)
f += s + '\n'
# Recursive call till all the next phases scheduled
for next_phase in phase.next_phase_ids:
if next_phase.state in ['draft', 'open', 'pending']:
rf, rphase_ids = self.generate_phase(cr, uid, [next_phase.id], f = '', parent=phase, context=context)
f += rf +'\n'
phase_ids += rphase_ids
else:
continue
return f, phase_ids
def schedule_tasks(self, cr, uid, ids, context=None):
"""
Schedule tasks base on faces lib
"""
if context is None:
context = {}
if type(ids) in (long, int,):
ids = [ids]
task_pool = self.pool.get('project.task')
resource_pool = self.pool.get('resource.resource')
for phase in self.browse(cr, uid, ids, context=context):
project = phase.project_id
calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
start_date = project.date_start
#Creating resources using the member of the Project
u_ids = [i.id for i in project.members]
resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
func_str = ''
start = start_date
minimum_time_unit = 1
# default values
working_hours_per_day = 24
working_days_per_week = 7
working_days_per_month = 30
working_days_per_year = 365
vacation = []
if calendar_id:
working_hours_per_day = 8 #TODO: it should be come from calendars
working_days_per_week = 5
working_days_per_month = 20
working_days_per_year = 200
vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
cls_str = ''
# Creating Resources for the Project
for key, vals in resource_objs.items():
cls_str +='''
class Resource_%s(Resource):
title = \"%s\"
vacation = %s
efficiency = %s
'''%(key, vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
# Create a new project for each phase
func_str += '''
def Phase_%d():
from resource.faces import Resource
title = \"%s\"
start = \'%s\'
minimum_time_unit = %s
working_hours_per_day = %s
working_days_per_week = %s
working_days_per_month = %s
working_days_per_year = %s
vacation = %s
working_days = %s
'''%(phase.id, phase.name, start, minimum_time_unit, working_hours_per_day, working_days_per_week, working_days_per_month, working_days_per_year, vacation, working_days )
parent = False
task_ids = []
todo_task_ids = task_pool.search(cr, uid, [('id', 'in', map(lambda x : x.id, phase.task_ids)),
('state', 'in', ['draft', 'open', 'pending'])
], order='sequence')
for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
func_str += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=True, context=context)
if not parent:
parent = task
task_ids.append(task.id)
func_str += cls_str
phase_id = phase.id
#check known constraints before running Face algorithm in order to have the error translated
if not phase.project_id.date_start:
raise osv.except_osv(_('Error !'),_('Task Scheduling is not possible.\nProject should have the Start date for scheduling.'))
# Allocating Memory for the required Project and Pahses and Resources
exec(func_str)
Phase = eval('Phase_%d' % phase.id)
phase = None
try:
phase = Task.BalancedProject(Phase)
except Exception, e:
raise osv.except_osv(_('Error !'),e)
for task_id in task_ids:
task = eval("phase.Task_%d" % task_id)
start_date = task.start.to_datetime()
end_date = task.end.to_datetime()
task_pool.write(cr, uid, [task_id], {
'date_start': start_date.strftime('%Y-%m-%d %H:%M:%S'),
'date_end': end_date.strftime('%Y-%m-%d %H:%M:%S')
}, context=context)
return True
return result
project_phase()
class project_resource_allocation(osv.osv):
_name = 'project.resource.allocation'
_description = 'Project Resource Allocation'
_rec_name = 'resource_id'
def get_name(self, cr, uid, ids, field_name, arg, context=None):
res = {}
for allocation in self.browse(cr, uid, ids, context=context):
name = allocation.phase_id.name
name += ' (%s%%)' %(allocation.useability)
res[allocation.id] = name
return res
class project_user_allocation(osv.osv):
_name = 'project.user.allocation'
_description = 'Phase User Allocation'
_rec_name = 'user_id'
_columns = {
'name': fields.function(get_name, type='char', size=256),
'resource_id': fields.many2one('resource.resource', 'Resource', required=True),
'user_id': fields.many2one('res.users', 'User', required=True),
'phase_id': fields.many2one('project.phase', 'Project Phase', ondelete='cascade', required=True),
'project_id': fields.related('phase_id', 'project_id', type='many2one', relation="project.project", string='Project', store=True),
'user_id': fields.related('resource_id', 'user_id', type='many2one', relation="res.users", string='User'),
'date_start': fields.date('Start Date', help="Starting Date"),
'date_end': fields.date('End Date', help="Ending Date"),
'useability': fields.float('Availability', help="Availability of this resource for this project phase in percentage (=50%)"),
'date_start': fields.datetime('Start Date', help="Starting Date"),
'date_end': fields.datetime('End Date', help="Ending Date"),
}
_defaults = {
'useability': 100,
}
project_resource_allocation()
project_user_allocation()
class project(osv.osv):
_inherit = "project.project"
@@ -420,161 +225,102 @@ class project(osv.osv):
'phase_ids': fields.one2many('project.phase', 'project_id', "Project Phases"),
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
}
def generate_members(self, cr, uid, ids, context=None):
"""
Return a list of Resource Class objects for the resources allocated to the phase.
"""
res = {}
resource_pool = self.pool.get('resource.resource')
for project in self.browse(cr, uid, ids, context=context):
user_ids = map(lambda x:x.id, project.members)
calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
resource_objs = resource_pool.generate_resources(cr, uid, user_ids, calendar_id, context=context)
res[project.id] = resource_objs
return res
def schedule_phases(self, cr, uid, ids, context=None):
"""
Schedule phase base on faces lib
"""
if context is None:
context = {}
def _schedule_header(self, cr, uid, ids, context=None):
context = context or {}
if type(ids) in (long, int,):
ids = [ids]
phase_pool = self.pool.get('project.phase')
task_pool = self.pool.get('project.task')
projects = self.browse(cr, uid, ids, context=context)
for project in projects:
if not project.members:
raise osv.except_osv(_('Warning !'),_("You must assign members on the project '%s' !") % (project.name,))
resource_pool = self.pool.get('resource.resource')
data_pool = self.pool.get('ir.model.data')
resource_allocation_pool = self.pool.get('project.resource.allocation')
data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
#Checking the Valid Phase resource allocation from project member
for project in self.browse(cr, uid, ids, context=context):
flag = False
res_missing = []
members_ids = []
if project.members:
members_ids = [user.id for user in project.members]
for phase in project.phase_ids:
if phase.resource_ids:
res_ids = [ re.id for re in phase.resource_ids]
for res in resource_allocation_pool.browse(cr, uid, res_ids, context=context):
if res.resource_id.user_id.id not in members_ids:
res_missing += [res.resource_id.name]
flag = True
if flag:
raise osv.except_osv(_('Warning !'),_("Resource(s) %s is(are) not member(s) of the project '%s' .") % (",".join(res_missing), project.name))
result = "from resource.faces import *\n"
result += "import datetime\n"
for project in self.browse(cr, uid, ids, context=context):
root_phase_ids = phase_pool.search(cr, uid, [('project_id', '=', project.id),
('state', 'in', ['draft', 'open', 'pending']),
('previous_phase_ids', '=', False)
])
calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
start_date = project.date_start
#if start_date:
# start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
#Creating resources using the member of the Project
u_ids = [i.id for i in project.members]
for task in project.tasks:
if task.state in ('done','cancelled'):
continue
if task.user_id and (task.user_id.id not in u_ids):
u_ids.append(task.user_id.id)
calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
func_str = ''
start = start_date
minimum_time_unit = 1
# default values
working_hours_per_day = 24
working_days_per_week = 7
working_days_per_month = 30
working_days_per_year = 365
vacation = []
if calendar_id:
working_hours_per_day = 8 #TODO: it should be come from calendars
working_days_per_week = 5
working_days_per_month = 20
working_days_per_year = 200
vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
cls_str = ''
# Creating Resources for the Project
for key, vals in resource_objs.items():
cls_str +='''
class Resource_%s(Resource):
title = \"%s\"
vacation = %s
efficiency = %s
'''%(key, vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
# Create a new project for each phase
func_str += '''
def Project_%d():
from resource.faces import Resource
title = \"%s\"
result +='''
class User_%s(Resource):
efficiency = %s
''' % (key, vals.get('efficiency', False))
result += '''
def Project():
'''
return result
def _schedule_project(self, cr, uid, project, context=None):
resource_pool = self.pool.get('resource.resource')
calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
# TODO: check if we need working_..., default values are ok.
result = """
def Project_%d():
start = \'%s\'
minimum_time_unit = %s
working_hours_per_day = %s
working_days_per_week = %s
working_days_per_month = %s
working_days_per_year = %s
working_days = %s
resource = %s
""" % (
project.id,
project.date_start, working_days,
'|'.join(['User_'+str(x.id) for x in project.members])
)
vacation = calendar_id and tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context)) or False
if vacation:
result+= """
vacation = %s
working_days = %s
'''%(project.id, project.name, start, minimum_time_unit, working_hours_per_day, working_days_per_week, working_days_per_month, working_days_per_year, vacation, working_days )
func_str += cls_str
phase_ids = []
for root_phase in phase_pool.browse(cr, uid, root_phase_ids, context=context):
phases, child_phase_ids = phase_pool.generate_phase(cr, uid, [root_phase.id], '', context=context)
func_str += phases
phase_ids += child_phase_ids
project_id = project.id
if not project.date_start:
raise osv.except_osv(_('Error !'),_('Task Scheduling is not possible.\nProject should have the Start date for scheduling.'))
# Allocating Memory for the required Project and Phases and Resources
exec(func_str)
Project = eval('Project_%d' % project.id)
project = None
try:
project = Task.BalancedProject(Project)
except Exception, e:
raise osv.except_osv(_('Error !'), e)
for phase_id in phase_ids:
act_phase = phase_pool.browse(cr, uid, phase_id, context=context)
resources = act_phase.resource_ids
phase = eval("project.Phase_%d" % phase_id)
start_date = phase.start.to_datetime()
end_date = phase.end.to_datetime()
if resources:
for res in resources:
vals = {}
vals.update({'date_start' : start_date })
vals.update({'date_end' : end_date})
resource_allocation_pool.write(cr, uid, res.id, vals, context=context)
if act_phase.task_ids:
for task in act_phase.task_ids:
vals = {}
#Getting values of the Tasks
temp = eval("phase.Task_%s"%task.id)
if temp.booked_resource:
res_name = temp.booked_resource[0].title
res_id = resource_pool.search(cr, uid,[('name','=',res_name)], context = context)
if res_id:
res = resource_pool.browse(cr, uid, res_id[0], context = context)
vals.update({'user_id' : res.user_id.id})
vals.update({'date_start' : temp.start.strftime('%Y-%m-%d %H:%M:%S')})
vals.update({'date_end' : temp.end.strftime('%Y-%m-%d %H:%M:%S')})
task_pool.write(cr, uid, task.id, vals, context=context)
""" % ( vacation, )
return result
phase_pool.write(cr, uid, [phase_id], {
'date_start': start_date.strftime('%Y-%m-%d'),
'date_end': end_date.strftime('%Y-%m-%d')
}, context=context)
return True
def schedule_phases(self, cr, uid, ids, context=None):
context = context or {}
if type(ids) in (long, int,):
ids = [ids]
projects = self.browse(cr, uid, ids, context=context)
result = self._schedule_header(cr, uid, ids, context=context)
for project in projects:
result += self._schedule_project(cr, uid, project, context=context)
result += self.pool.get('project.phase').generate_phase(cr, uid, project.phase_ids, context=context)
local_dict = {}
exec result in local_dict
projects_gantt = Task.BalancedProject(local_dict['Project'])
for project in projects:
project_gantt = getattr(projects_gantt, 'Project_%d' % (project.id,))
for phase in project.phase_ids:
if phase.state in ('done','cancelled'):
continue
# Maybe it's better to update than unlink/create if it already exists ?
p = getattr(project_gantt, 'Phase_%d' % (phase.id,))
self.pool.get('project.user.allocation').unlink(cr, uid,
[x.id for x in phase.user_ids],
context=context
)
for r in p.booked_resource:
self.pool.get('project.user.allocation').create(cr, uid, {
'user_id': int(r.name[5:]),
'phase_id': phase.id,
'date_start': p.start.strftime('%Y-%m-%d %H:%M:%S'),
'date_end': p.end.strftime('%Y-%m-%d %H:%M:%S')
}, context=context)
self.pool.get('project.phase').write(cr, uid, [phase.id], {
'date_start': p.start.strftime('%Y-%m-%d %H:%M:%S'),
'date_end': p.end.strftime('%Y-%m-%d %H:%M:%S')
}, context=context)
return True
#TODO: DO Resource allocation and compute availability
def compute_allocation(self, rc, uid, ids, start_date, end_date, context=None):
@@ -584,186 +330,67 @@ def Project_%d():
return allocation
def schedule_tasks(self, cr, uid, ids, context=None):
"""
Schedule task base on faces lib
"""
if context is None:
context = {}
context = context or {}
if type(ids) in (long, int,):
ids = [ids]
task_pool = self.pool.get('project.task')
resource_pool = self.pool.get('resource.resource')
data_pool = self.pool.get('ir.model.data')
data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
projects = self.browse(cr, uid, ids, context=context)
result = self._schedule_header(cr, uid, ids, context=context)
for project in projects:
result += self._schedule_project(cr, uid, project, context=context)
result += self.pool.get('project.task')._generate_task(cr, uid, project.tasks, ident=4, context=context)
for project in self.browse(cr, uid, ids, context=context):
calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
start_date = project.date_start
#Checking the Valid Phase resource allocation from project member
flag = False
res_missing = []
members_ids = []
if project.members:
members_ids = [user.id for user in project.members]
for phase in project.phase_ids:
if phase.resource_ids:
res_ids = [ re.id for re in phase.resource_ids]
for res in self.pool.get('project.resource.allocation').browse(cr, uid, res_ids, context=context):
if res.resource_id.user_id.id not in members_ids:
res_missing += [res.resource_id.name]
flag = True
if flag:
raise osv.except_osv(_('Warning !'),_("Resource(s) %s is(are) not member(s) of the project '%s' .") % (",".join(res_missing), project.name))
#Creating resources using the member of the Project
u_ids = [i.id for i in project.members]
resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
try:
start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
except:
raise osv.except_osv(_('Error !'),_('Task Scheduling is not possible.\nProject should have the Start date for scheduling.'))
func_str = ''
start = start_date
minimum_time_unit = 1
# default values
working_hours_per_day = 24
working_days_per_week = 7
working_days_per_month = 30
working_days_per_year = 365
vacation = []
if calendar_id:
working_hours_per_day = 8 #TODO: it should be come from calendars
working_days_per_week = 5
working_days_per_month = 20
working_days_per_year = 200
vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
local_dict = {}
exec result in local_dict
projects_gantt = Task.BalancedProject(local_dict['Project'])
working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
cls_str = ''
# Creating Resources for the Project
for key, vals in resource_objs.items():
cls_str +='''
class Resource_%s(Resource):
title = \"%s\"
vacation = %s
efficiency = %s
'''%(key, vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
# Create a new project for each phase
func_str += '''
def Project_%d():
from resource.faces import Resource
title = \"%s\"
start = \'%s\'
minimum_time_unit = %s
working_hours_per_day = %s
working_days_per_week = %s
working_days_per_month = %s
working_days_per_year = %s
vacation = %s
working_days = %s
'''%(project.id, project.name, start, minimum_time_unit, working_hours_per_day, working_days_per_week, working_days_per_month, working_days_per_year, vacation, working_days )
parent = False
task_ids = []
todo_task_ids = task_pool.search(cr, uid, [('project_id', '=', project.id),
('state', 'in', ['draft', 'open', 'pending'])
], order='sequence')
if todo_task_ids:
for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
func_str += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=True,context=context)
if not parent:
parent = task
task_ids.append(task.id)
func_str += cls_str
for project in projects:
project_gantt = getattr(projects_gantt, 'Project_%d' % (project.id,))
for task in project.tasks:
if task.state in ('done','cancelled'):
continue
if not project.date_start:# or not project.members:
raise osv.except_osv(_('Error !'),_('Task Scheduling is not possible.\nProject should have the Start date for scheduling.'))
# Allocating Memory for the required Project and Phases and Resources
exec(func_str)
Project = eval('Project_%d' % project.id)
project = None
try:
project = Task.BalancedProject(Project)
except Exception, e:
raise osv.except_osv(_('Error !'), e)
for task_id in task_ids:
task = eval("project.Task_%d" % task_id)
start_date = task.start.to_datetime()
end_date = task.end.to_datetime()
task_pool.write(cr, uid, [task_id], {
'date_start': start_date.strftime('%Y-%m-%d %H:%M:%S'),
'date_end': end_date.strftime('%Y-%m-%d %H:%M:%S')
}, context=context)
p = getattr(project_gantt, 'Task_%d' % (task.id,))
self.pool.get('project.task').write(cr, uid, [task.id], {
'date_start': p.start.strftime('%Y-%m-%d %H:%M:%S'),
'date_end': p.end.strftime('%Y-%m-%d %H:%M:%S')
}, context=context)
if (not task.user_id) and (p.booked_resource):
self.pool.get('project.task').write(cr, uid, [task.id], {
'user_id': int(p.booked_resource[0].name[5:]),
}, context=context)
return True
project()
class resource_resource(osv.osv):
_inherit = "resource.resource"
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
if context is None:
context = {}
if context.get('project_id',False):
project_pool = self.pool.get('project.project')
project_rec = project_pool.browse(cr, uid, context['project_id'], context=context)
user_ids = [user_id.id for user_id in project_rec.members]
args.append(('user_id','in',user_ids))
return super(resource_resource, self).search(cr, uid, args, offset, limit, order, context, count)
resource_resource()
class project_task(osv.osv):
_inherit = "project.task"
_columns = {
'phase_id': fields.many2one('project.phase', 'Project Phase'),
}
_defaults = {
'user_id' : False
}
def _generate_task(self, cr, uid, tasks, ident=4, context=None):
context = context or {}
result = ""
ident = ' '*ident
for task in tasks:
if task.state in ('done','cancelled'):
continue
result += '''
%sdef Task_%s():
%s todo = \"%.2fH\"
%s effort = \"%.2fH\"''' % (ident,task.id, ident,task.remaining_hours, ident,task.total_hours)
start = []
for t2 in task.parent_ids:
start.append("up.Task_%s.end" % (t2.id,))
if start:
result += '''
%s start = max(%s)
''' % (ident,','.join(start))
def generate_task(self, cr, uid, task_id, parent=False, flag=False, context=None):
if context is None:
context = {}
task = self.browse(cr, uid, task_id, context=context)
duration = str(task.planned_hours)+ 'H'
str_resource = False
parent = task.parent_ids
if task.phase_id.resource_ids:
str_resource = ('%s | '*len(task.phase_id.resource_ids))[:-2]
str_resource = str_resource % tuple(map(lambda x: 'Resource_%s'%x.resource_id.id, task.phase_id.resource_ids))
# Task Defination for the Phase of the Project
if not flag:
s = '''
def Task_%s():
title = \"%s\"
effort = \'%s\'
resource = %s
'''%(task.id, task.name, duration, str_resource)
if task.child_ids:
seq = [[child.planned_hours, child.id] for child in task.child_ids]
seq.sort(key=itemgetter(0))
s +='''
start = up.Task_%s.end
'''%(seq[-1][1])
else:
s = '''
def Task_%s():
title = \"%s\"
effort = \'%s\'
resource = %s
'''%(task.id, task.name, duration, str_resource)
if task.child_ids:
seq = [[child.planned_hours, child.id] for child in task.child_ids]
seq.sort(key=itemgetter(0))
s +='''
start = up.Task_%s.end
'''%(seq[-1][1])
s += '\n'
return s
if task.user_id:
result += '''
%s resource = %s
''' % (ident, 'User_'+str(task.user_id.id))
result += "\n"
return result
project_task()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,55 +1,9 @@
<?xml version="1.0" ?>
<openerp>
<data noupdate="1">
<!--
This Demo data file Human Resources, Phases and Resources,Tasks allocation and also run scheduling of phase and tasks.
-->
<!-- Human Resources -->
<record id="resource_analyst" model="resource.resource">
<field name="user_id" ref="project.res_users_analyst"/>
<field name="name">Analyst</field>
<field name="calendar_id" ref="resource.timesheet_group1"/>
</record>
<record id="resource_project_manager" model="resource.resource">
<field name="user_id" ref="project.res_users_project_manager"/>
<field name="name">Project Manager</field>
<field name="calendar_id" ref="resource.timesheet_group1"/>
</record>
<record id="resource_technical_leader" model="resource.resource">
<field name="user_id" ref="project.res_users_technical_leader"/>
<field name="name">Technical Leader</field>
<field name="calendar_id" ref="resource.timesheet_group1"/>
</record>
<record id="resource_developer" model="resource.resource">
<field name="user_id" ref="project.res_users_developer"/>
<field name="name">Developer</field>
<field name="calendar_id" ref="resource.timesheet_group1"/>
</record>
<record id="resource_designer" model="resource.resource">
<field name="user_id" ref="project.res_users_designer"/>
<field name="name">Designer</field>
<field name="calendar_id" ref="resource.timesheet_group1"/>
</record>
<record id="resource_tester" model="resource.resource">
<field name="user_id" ref="project.res_users_tester"/>
<field name="name">Tester</field>
<field name="calendar_id" ref="resource.timesheet_group1"/>
</record>
<record id="resource_finacial_manager" model="resource.resource">
<field name="user_id" ref="project.res_users_finacial_manager"/>
<field name="name">Project Finacial Manager</field>
<field name="calendar_id" ref="resource.timesheet_group1"/>
</record>
<!--
This Demo data file Human Resources, Phases and Resources,Tasks allocation and also run scheduling of phase and tasks.
-->
<!-- Project Phases -->
<record id="project_phase_1" model="project.phase">
@@ -58,7 +12,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
<field name="product_uom" ref="product.uom_day"/>
<field eval="1" name="sequence"/>
<field name="duration">30</field>
<field name="responsible_id" ref="project.res_users_analyst"/>
<field name="project_id" ref="project.project_integrate_openerp"/>
</record>
@@ -68,7 +21,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
<field name="product_uom" ref="product.uom_day"/>
<field eval="2" name="sequence"/>
<field name="duration">20</field>
<field name="responsible_id" ref="project.res_users_project_manager"/>
<field name="project_id" ref="project.project_integrate_openerp"/>
</record>
@@ -78,7 +30,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
<field name="product_uom" ref="product.uom_day"/>
<field eval="3" name="sequence"/>
<field name="duration">20</field>
<field name="responsible_id" ref="project.res_users_project_manager"/>
<field name="project_id" ref="project.project_integrate_openerp"/>
</record>
@@ -88,7 +39,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
<field name="product_uom" ref="product.uom_day"/>
<field eval="4" name="sequence"/>
<field name="duration">90</field>
<field name="responsible_id" ref="project.res_users_technical_leader"/>
<field name="project_id" ref="project.project_integrate_openerp"/>
</record>
@@ -98,7 +48,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
<field name="product_uom" ref="product.uom_day"/>
<field eval="5" name="sequence"/>
<field name="duration">30</field>
<field name="responsible_id" ref="project.res_users_project_manager"/>
<field name="project_id" ref="project.project_integrate_openerp"/>
</record>
@@ -108,56 +57,9 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
<field name="product_uom" ref="product.uom_day"/>
<field eval="5" name="sequence"/>
<field name="duration">10</field>
<field name="responsible_id" ref="project.res_users_analyst"/>
<field name="project_id" ref="project.project_integrate_openerp"/>
</record>
<!-- Project Resource Allocation -->
<record id="resource_allocation_1" model="project.resource.allocation">
<field name="resource_id" ref="resource_analyst"/>
<field name="phase_id" ref="project_phase_1"/>
</record>
<record id="resource_allocation_2" model="project.resource.allocation">
<field name="resource_id" ref="resource_project_manager"/>
<field name="phase_id" ref="project_phase_2"/>
</record>
<record id="resource_allocation_3" model="project.resource.allocation">
<field name="resource_id" ref="resource_project_manager"/>
<field name="phase_id" ref="project_phase_3"/>
</record>
<record id="resource_allocation_4" model="project.resource.allocation">
<field name="resource_id" ref="resource_technical_leader"/>
<field name="phase_id" ref="project_phase_4"/>
</record>
<record id="resource_allocation_5" model="project.resource.allocation">
<field name="resource_id" ref="resource_developer"/>
<field name="phase_id" ref="project_phase_4"/>
</record>
<record id="resource_allocation_6" model="project.resource.allocation">
<field name="resource_id" ref="resource_designer"/>
<field name="phase_id" ref="project_phase_4"/>
</record>
<record id="resource_allocation_7" model="project.resource.allocation">
<field name="resource_id" ref="resource_tester"/>
<field name="phase_id" ref="project_phase_4"/>
</record>
<record id="resource_allocation_8" model="project.resource.allocation">
<field name="resource_id" ref="resource_project_manager"/>
<field name="phase_id" ref="project_phase_5"/>
</record>
<record id="resource_allocation_9" model="project.resource.allocation">
<field name="resource_id" ref="resource_analyst"/>
<field name="phase_id" ref="project_phase_6"/>
</record>
<!-- Tasks -->
<record id="project.project_task_1" model="project.task">
@@ -2,32 +2,32 @@
<openerp>
<data>
<menuitem id="base.menu_project_long_term" name="Long Term Planning" parent="base.menu_main_pm" sequence="1"/>
<menuitem id="base.menu_project_long_term" name="Planning" parent="base.menu_main_pm" sequence="1"/>
# ------------------------------------------------------
# Project Resource Allocation
# Project User Allocation
# ------------------------------------------------------
<record id="view_project_resource_allocation_gantt" model="ir.ui.view">
<field name="name">project.resource.allocation.gantt</field>
<field name="model">project.resource.allocation</field>
<record id="view_project_user_allocation_gantt" model="ir.ui.view">
<field name="name">project.user.allocation.gantt</field>
<field name="model">project.user.allocation</field>
<field name="type">gantt</field>
<field name="arch" type="xml">
<gantt color="project_id" date_start="date_start" date_stop="date_end" mode="year">
<level object="res.users" link="user_id" domain="[]">
<field name="name"/>
<field name="phase_id"/>
</level>
</gantt>
</field>
</record>
<record id="view_project_resource_allocation_calendar" model="ir.ui.view">
<field name="name">project.resource.allocation.calendar</field>
<field name="model">project.resource.allocation</field>
<record id="view_project_user_allocation_calendar" model="ir.ui.view">
<field name="name">project.user.allocation.calendar</field>
<field name="model">project.user.allocation</field>
<field name="type">calendar</field>
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<calendar color="resource_id" date_start="date_start" date_stop="date_end" day_length="12" string="Resources">
<calendar color="user_id" date_start="date_start" date_stop="date_end" day_length="12" string="Users">
<field name="phase_id"/>
<field name="project_id"/>
</calendar>
@@ -35,53 +35,51 @@
</record>
<record id="view_project_resource_allocation_form" model="ir.ui.view">
<field name="name">project.resource.allocation.form</field>
<field name="model">project.resource.allocation</field>
<record id="view_project_user_allocation_form" model="ir.ui.view">
<field name="name">project.user.allocation.form</field>
<field name="model">project.user.allocation</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Project Resource Allocation">
<form string="Project User Allocation">
<group colspan="4" col="6">
<field name="resource_id"/>
<field name="user_id"/>
<field name="phase_id"/>
<field name="project_id"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="useability" string="Availability"/>
</group>
</form>
</field>
</record>
<record id="view_project_resource_allocation_list" model="ir.ui.view">
<field name="name">project.resource.allocation.list</field>
<field name="model">project.resource.allocation</field>
<record id="view_project_user_allocation_list" model="ir.ui.view">
<field name="name">project.user.allocation.list</field>
<field name="model">project.user.allocation</field>
<field name="type">tree</field>
<field name="priority" eval="5"/>
<field name="arch" type="xml">
<tree editable="bottom" string="Project Resource Allocation">
<field name="resource_id"/>
<tree editable="bottom" string="Project User Allocation">
<field name="user_id"/>
<field name="phase_id"/>
<field name="project_id"/>
<field name="useability" string="Availability"/>
</tree>
</field>
</record>
<record id="view_project_resource_allocation_search" model="ir.ui.view">
<field name="name">project.resource.allocation.search</field>
<field name="model">project.resource.allocation</field>
<record id="view_project_user_allocation_search" model="ir.ui.view">
<field name="name">project.user.allocation.search</field>
<field name="model">project.user.allocation</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Resources Allocation">
<search string="Planning of Users">
<group>
<field name="resource_id"/>
<field name="user_id"/>
<field name="phase_id"/>
<field name="project_id"/>
</group>
<newline/>
<group expand="0" string="Group By...">
<filter name="resource" string="Resource" icon="terp-personal" domain="[]" context="{'group_by':'resource_id'}"/>
<filter name="user" string="User" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<separator orientation="vertical"/>
<filter string="Project" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}"/>
<filter string="Phase" icon="terp-project" domain="[]" context="{'group_by':'phase_id'}"/>
@@ -90,12 +88,12 @@
</field>
</record>
<record id="act_resouce_allocation" model="ir.actions.act_window">
<field name="name">Resources Allocation</field>
<field name="res_model">project.resource.allocation</field>
<field name="name">Planning of Users</field>
<field name="res_model">project.user.allocation</field>
<field name="view_type">form</field>
<field name="view_mode">gantt,tree,form,calendar</field>
<field name="context">{}</field>
<field name="search_view_id" ref="view_project_resource_allocation_search"/>
<field name="search_view_id" ref="view_project_user_allocation_search"/>
</record>
@@ -110,32 +108,27 @@
<field name="arch" type="xml">
<form string="Project Phase">
<group colspan="6" col="6">
<group colspan="6" col="6" >
<field name="name" select="1"/>
<field name="project_id" on_change="onchange_project(project_id)"/>
<field name="responsible_id"/>
</group>
<group colspan="6" col="6">
<field name="date_start" readonly="1"/>
<group colspan="6" col="7" >
<field name="name" colspan="4"/>
<field name="project_id" on_change="onchange_project(project_id)" colspan="3"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="duration"/>
<field name="product_uom" nolabel="1" domain="[('category_id.name', '=', 'Working Time')]"/>
<field name="date_end" readonly="1"/>
<field name="product_uom" nolabel="1"/>
</group>
</group>
<notebook colspan="4">
<page string="Resource Allocation">
<field colspan="4" name="resource_ids" nolabel="1">
<tree editable="bottom" string="Project Resource Allocation">
<field name="resource_id" context="{'project_id':parent.project_id}"/>
<page string="Planning of Users">
<field colspan="4" name="user_ids" nolabel="1">
<tree editable="bottom" string="Project Users">
<field name="user_id"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="useability" string="Availability"/>
</tree>
<form string="Project Resource Allocation">
<field name="resource_id" context="{'project_id':parent.project_id}"/>
<form string="Project Users">
<field name="user_id"/>
<field name="date_start" />
<field name="date_end"/>
<field name="useability" string="Availability"/>
</form>
</field>
<separator string="" colspan="4"/>
@@ -162,38 +155,9 @@
<field name="remaining_hours" widget="float_time" sum="Remaining Hours"/>
<field name="state"/>
</tree>
<form string="Project's Tasks">
<notebook colspan="4">
<page string="Task Detail">
<group colspan="4" col="4">
<field name="name"/>
<field name="project_id"/>
<field name="user_id"/>
<field name="sequence"/>
</group>
<group colspan="2" col="2">
<separator string="Dates" colspan="2"/>
<field name="date_start"/>
<field name="date_end" />
<field name="date_deadline"/>
</group>
<group colspan="2" col="2">
<separator string="Planning" colspan="2"/>
<field name="planned_hours" widget="float_time"/>
<field name="total_hours" widget="float_time"/>
<field name="remaining_hours" widget="float_time"/>
<field name="effective_hours" invisible="1"/>
</group>
<group colspan="6" col="4">
<field name="state"/>
</group>
</page>
</notebook>
</form>
</field>
<button name="%(action_project_schedule_tasks)d" string="Schedule Tasks" type="action" icon="gtk-jump-to"/>
</page>
<page string="Schedule and Display info">
<page string="Constraints">
<group colspan="2" col="2">
<separator string="Constraints" colspan="2"/>
<field name="constraint_date_start"/>
@@ -203,10 +167,12 @@
<separator string="Displaying settings" colspan="2"/>
<field name="sequence"/>
</group>
<separator colspan="4" string="Force Assigned Users"/>
<field colspan="4" name="user_force_ids" nolabel="1"/>
<separator colspan="4" string="Previous Phases"/>
<field colspan="4" name="previous_phase_ids" nolabel="1" domain="[('id','!=',active_id)]"/>
<field colspan="4" name="previous_phase_ids" nolabel="1"/>
<separator colspan="4" string="Next Phases"/>
<field colspan="4" name="next_phase_ids" nolabel="1" domain="[('id','!=',active_id)]"/>
<field colspan="4" name="next_phase_ids" nolabel="1"/>
</page>
</notebook>
<newline/>
@@ -223,12 +189,10 @@
<tree colors="grey:state in ('cancelled','done');blue:state == 'pending'" string="Project Phases">
<field name="name"/>
<field name="project_id" on_change="onchange_project(project_id)"/>
<field name="responsible_id"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="duration"/>
<field name="state"/>
<button name="%(action_project_schedule_tasks)d" string="Schedule Tasks" type="action" icon="gtk-jump-to"/>
</tree>
</field>
</record>
@@ -241,7 +205,6 @@
<field name="arch" type="xml">
<calendar color="project_id" date_start="date_start" date_stop="date_end" day_length="12">
<field name="name"/>
<field name="responsible_id"/>
</calendar>
</field>
</record>
@@ -275,14 +238,11 @@
<field name="project_id">
<filter domain="[('project_id.user_id','=',uid)]" help="My Projects" icon="terp-folder-violet"/>
</field>
<field name="responsible_id"/>
<field name="date_start"/>
<field name="date_end"/>
</group>
<newline/>
<group expand="0" string="Group By..." groups="base.group_extended">
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'responsible_id'}"/>
<separator orientation="vertical"/>
<filter string="Project" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}" name="project"/>
<separator orientation="vertical"/>
<filter string="State" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
@@ -300,7 +260,7 @@
<field name="view_mode">gantt,tree,form,calendar</field>
<field name="context">{"search_default_current": 1}</field>
<field name="search_view_id" ref="view_project_phase_search"/>
<field name="help">A project can be split into the different phases. For each phase, you can define your resources allocation, describe different tasks and link your phase to previous and next phases, add date constraints for the automated scheduling. Use the long term planning in order to planify your available human resources, convert your phases into a series of tasks when you start working on the project.</field>
<field name="help">A project can be split into the different phases. For each phase, you can define your users allocation, describe different tasks and link your phase to previous and next phases, add date constraints for the automated scheduling. Use the long term planning in order to planify your available users, convert your phases into a series of tasks when you start working on the project.</field>
</record>
<record id="act_project_phase_list" model="ir.actions.act_window">
@@ -317,7 +277,7 @@
# Project
# ------------------------------------------------------
<record id="project.view_project_resource_form1" model="ir.ui.view">
<record id="project.view_project_user_form1" model="ir.ui.view">
<field name="name">Project Resource Calendar View</field>
<field name="model">project.project</field>
<field name="type">form</field>
@@ -402,17 +362,6 @@
view_type="form"
/>
<act_window
context="{'search_default_user_id': [user_id]}"
id="act_project_resource"
name="Resources"
res_model="resource.resource"
src_model="project.project"
domain="[('user_id', '=',user_id)]"
view_mode="tree,form"
view_type="form"
/>
# ------------------------------------------------------
# Menu Items
# ------------------------------------------------------
@@ -428,16 +377,16 @@
<menuitem id="menu_resouce_allocation" action="act_resouce_allocation"
icon="STOCK_INDENT"
name="Resource Allocations" parent="base.menu_project_long_term" sequence="2"/>
parent="base.menu_project_long_term" sequence="2"/>
<menuitem id="menu_pm_resources_project1"
<menuitem id="menu_pm_users_project1"
groups="base.group_extended"
name="Resources" parent="project.menu_definitions" sequence="3"/>
name="Users" parent="project.menu_definitions" sequence="3"/>
<menuitem id="menu_phase_schedule" name="Scheduling" parent="base.menu_main_pm" sequence="4" groups="project.group_project_user,project.group_project_manager"/>
<menuitem action="resource.action_resource_resource_tree" id="menu_view_resource" parent="menu_pm_resources_project1" sequence="2"/>
<menuitem action="resource.action_resource_calendar_form" id="menu_view_resource_calendar" parent="menu_pm_resources_project1" sequence="1"/>
<menuitem action="resource.action_resource_calendar_leave_tree" id="menu_view_resource_calendar_leaves" parent="menu_pm_resources_project1" sequence="1"/>
<menuitem action="resource.action_resource_resource_tree" id="menu_view_resource" parent="menu_pm_users_project1" sequence="2"/>
<menuitem action="resource.action_resource_calendar_form" id="menu_view_resource_calendar" parent="menu_pm_users_project1" sequence="1"/>
<menuitem action="resource.action_resource_calendar_leave_tree" id="menu_view_resource_calendar_leaves" parent="menu_pm_users_project1" sequence="1"/>
</data>
</openerp>
@@ -1,9 +1,9 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_project_phase","project.phase","model_project_phase","project.group_project_user",1,1,1,0
"access_project_resource_allocation","project.resource.allocation","model_project_resource_allocation","project.group_project_user",1,0,0,0
"access_project_user_allocation","project.user.allocation","model_project_user_allocation","project.group_project_user",1,0,0,0
"access_project_phase_manager","project.phase manager","model_project_phase","project.group_project_manager",1,1,1,1
"access_project_resource_allocation_manager","project.resource.allocation manager","model_project_resource_allocation","project.group_project_manager",1,1,1,1
"access_resource_resource_user","resource.resource user","resource.model_resource_resource","project.group_project_user",1,0,0,0
"access_resource_calendar_leaves_user","resource.calendar.leaves user","resource.model_resource_calendar_leaves","project.group_project_user",1,1,1,1
"access_resource_resource_manager","resource.resource manager","resource.model_resource_resource","project.group_project_manager",1,1,1,1
"access_project_resource_allocation_manager","project.resource.allocation.manager","model_project_resource_allocation","project.group_project_manager",1,1,1,1
"access_project_user_allocation_manager","project.user.allocation manager","model_project_user_allocation","project.group_project_manager",1,1,1,1
"access_resource_resource_user","user.user user","resource.model_resource_resource","project.group_project_user",1,0,0,0
"access_resource_calendar_leaves_user","user.calendar.leaves user","resource.model_resource_calendar_leaves","project.group_project_user",1,1,1,1
"access_resource_resource_manager","user.user manager","resource.model_resource_resource","project.group_project_manager",1,1,1,1
"access_project_user_allocation_manager","project.user.allocation.manager","model_project_user_allocation","project.group_project_manager",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_project_phase project.phase model_project_phase project.group_project_user 1 1 1 0
3 access_project_resource_allocation access_project_user_allocation project.resource.allocation project.user.allocation model_project_resource_allocation model_project_user_allocation project.group_project_user 1 0 0 0
4 access_project_phase_manager project.phase manager model_project_phase project.group_project_manager 1 1 1 1
5 access_project_resource_allocation_manager access_project_user_allocation_manager project.resource.allocation manager project.user.allocation manager model_project_resource_allocation model_project_user_allocation project.group_project_manager 1 1 1 1
6 access_resource_resource_user resource.resource user user.user user resource.model_resource_resource project.group_project_user 1 0 0 0
7 access_resource_calendar_leaves_user resource.calendar.leaves user user.calendar.leaves user resource.model_resource_calendar_leaves project.group_project_user 1 1 1 1
8 access_resource_resource_manager resource.resource manager user.user manager resource.model_resource_resource project.group_project_manager 1 1 1 1
9 access_project_resource_allocation_manager access_project_user_allocation_manager project.resource.allocation.manager project.user.allocation.manager model_project_resource_allocation model_project_user_allocation project.group_project_manager 1 1 1 1
@@ -5,6 +5,8 @@
name: "Develop Yaml Project Module"
date_start: !eval time.strftime('%Y-%m-%d')
resource_calendar_id: resource.timesheet_group1
members:
- base.user_admin
-
And I create three human ressources to work on this project, an analyst.
-
@@ -43,7 +45,6 @@
name: "Analysis"
product_uom: product.uom_day
project_id: project_project_developyamlproject0
responsible_id: project.res_users_analyst
state: draft
-
@@ -4,6 +4,8 @@
!record {model: project.project, id: project_project_developyamlproject_2}:
name: "Develop Yaml Project Module"
date_start: !eval time.strftime('%Y-%m-%d')
members:
- base.user_admin
-
I have set Working Time from Monday to Friday from 9am to 17pm.
@@ -107,7 +109,6 @@
!record {model: project.compute.phases, id: project_compute_phases_0}:
project_id: project_project_developyamlproject_2
target_project: one
-
Schedule project phases using Compute Phase Scheduling
-
@@ -120,7 +121,7 @@
!python {model: project.project}: |
proj=self.browse(cr, uid, [ref("project_project_developyamlproject_2")])[0]
for phase in proj.phase_ids:
if (not phase.responsible_id) or (not phase.date_start) or (not phase.date_end):
raise AssertionError("Phases not scheduled")
if (not phase.date_start) or (not phase.date_end):
raise AssertionError("Phases not scheduled")
@@ -15,7 +15,6 @@
product_uom: product.uom_day
name: "Analysis Flow for Yaml"
project_id: project_project_developyamlproject_2
responsible_id: project.res_users_analyst
state: draft
-
@@ -71,7 +70,7 @@
!python {model: project.project}: |
proj=self.browse(cr, uid, [ref("project_project_developyamlproject_2")])[0]
for phase in proj.phase_ids:
if (not phase.responsible_id) or (not phase.date_start) or (not phase.date_end):
if (not phase.date_start) or (not phase.date_end):
raise AssertionError("Phases not scheduled")
@@ -1,90 +0,0 @@
-
Create project 'Develop an outlook-openerp synchronization plugin'
-
!record {model: project.project, id: project_project_project4}:
name: Develop an outlook-openerp synchronization plugin
members:
- project.res_users_project_manager
- project.res_users_technical_leader
- project.res_users_developer
- project.res_users_designer
- project.res_users_tester
- project.res_users_analyst
- project.res_users_finacial_manager
-
Create phase 'Develop GUI' in Outlook
-
!record {model: project.phase, id: project_phase_phase0}:
date_start: !eval time.strftime("%Y-%m-%d")
duration: 200.0
name: Develop GUI in Outlook
product_uom: product.uom_day
project_id: project_project_project4
state: draft
resource_ids:
- resource_id: project_long_term.resource_analyst
- resource_id: project_long_term.resource_developer
- resource_id: project_long_term.resource_designer
- resource_id: project_long_term.resource_tester
- resource_id: project_long_term.resource_project_manager
-
Create the phase task 'Develop GUI for Server Configuration'
-
!record {model: project.task, id: project_task_t0}:
name: Develop GUI for Server Configuration
planned_hours: 20.0
remaining_hours: 20.0
state: draft
phase_id: project_phase_phase0
project_id: project_project_project4
-
Create the phase task 'Develop GUI for Modules Configuration'
-
!record {model: project.task, id: project_task_t1}:
name: Develop GUI for Modules Configuration
planned_hours: 25.0
remaining_hours: 25.0
state: draft
phase_id: project_phase_phase0
project_id: project_project_project4
-
Create the phase task 'Develop GUI for OpenERP Synchronisation'
-
!record {model: project.task, id: project_task_t2}:
name: Develop GUI for OpenERP Synchronisation
planned_hours: 30.0
remaining_hours: 30.0
state: draft
phase_id: project_phase_phase0
project_id: project_project_project4
-
Create the phase task 'Design required GUI/Menus'
-
!record {model: project.task, id: project_task_3}:
name: Design required GUI/Menus
planned_hours: 25.0
remaining_hours: 25.0
state: draft
phase_id: project_phase_phase0
project_id: project_project_project4
-
Schedule phase tasks
-
!python {model: project.phase}: |
res = self.schedule_tasks(cr, uid, [ref("project_phase_phase0")])
-
Check if tasks scheduled, check that either of task's start_date, end_date and user_id is not null
-
!python {model: project.phase}: |
phase = self.browse(cr, uid, [ref("project_phase_phase0")])[0]
for task in phase.task_ids:
if (not task.date_start) or (not task.date_end):
raise AssertionError("Phase Tasks not scheduled: %d uid=%r start=%r end=%r" % \
(task.id, task.user_id, task.date_start, task.date_end))
@@ -31,28 +31,6 @@
name: Establishing Project Feasibility
product_uom: product.uom_day
project_id: project_project_worldbanksproject0
-
Resource1
-
!record {model: project.resource.allocation, id: res_phase1}:
resource_id: project_long_term.resource_project_manager
phase_id: project_phase_definingclientsbasicideaofproject0
-
Resource2
-
!record {model: project.resource.allocation, id: res_phase2}:
resource_id: project_long_term.resource_analyst
phase_id: project_phase_definingclientsbasicideaofproject0
-
Resource3
-
!record {model: project.resource.allocation, id: res_phase3}:
resource_id: project_long_term.resource_technical_leader
phase_id: project_phase_definingclientsbasicideaofproject0
-
Create the phase task 'Develop GUI for Server Configuration'
-
@@ -84,20 +62,6 @@
product_uom: product.uom_hour
project_id: project_project_worldbanksproject0
-
Resource4
-
!record {model: project.resource.allocation, id: res_phase4}:
resource_id: project_long_term.resource_developer
phase_id: project_phase_preparationofengineeringdesigns0
-
Resource5
-
!record {model: project.resource.allocation, id: res_phase5}:
resource_id: project_long_term.resource_designer
phase_id: project_phase_preparationofengineeringdesigns0
-
Create the phase task 'Develop GUI for Client Configuration'
-
@@ -210,7 +174,7 @@
!python {model: project.project}: |
prj=self.browse(cr, uid, [ref("project_project_worldbanksproject0")])[0]
for phase in prj.phase_ids:
if (not phase.responsible_id) or (not phase.date_start) or (not phase.date_end):
raise AssertionError("Tasks not scheduled")
if (not phase.date_start) or (not phase.date_end):
raise AssertionError("Tasks not scheduled")
@@ -11,26 +11,17 @@
- project.res_users_tester
- project.res_users_analyst
- project.res_users_finacial_manager
-
Create phase 'Develop GUI' in thunderbird
-
!record {model: project.phase, id: project_phase_phase1}:
date_start: !eval time.strftime('%Y-%m-%d')
date_start: !eval time.strftime('%Y-%m-%d %H:%M:%S')
duration: 200.0
name: Develop GUI in thunderbird
product_uom: product.uom_day
project_id: project_project_project1
state: draft
resource_ids:
- resource_id: project_long_term.resource_analyst
useability: 80.0
- resource_id: project_long_term.resource_developer
useability: 30.0
- resource_id: project_long_term.resource_designer
useability: 30.0
-
Create the phase task 'Develop GUI for Server Configuration'
-
@@ -76,8 +67,7 @@
phase_id: project_phase_phase1
project_id: project_project_project1
user_id: project.res_users_designer
-
Schedule tasks
-
@@ -88,7 +78,9 @@
Check if tasks scheduled, check that either of task's start_date, end_date and user_id is not null
-
!python {model: project.project}: |
prj = self.browse(cr, uid, [ref("project_project_project1")])[0]
for task in prj.tasks:
if (not task.user_id) or (not task.date_start) or (not task.date_end):
raise AssertionError("Project Tasks not scheduled")
prj = self.browse(cr, uid, [ref("project_project_project1")])[0]
for task in prj.tasks:
if task.state in ('done','cancelled'):
continue
if (not task.user_id) or (not task.date_start) or (not task.date_end):
raise AssertionError("Project tasks not scheduled!")
@@ -63,10 +63,12 @@
import datetime
from dateutil.relativedelta import *
start = (datetime.date.today()).strftime('%Y-%m-%d')
end = (datetime.date.today() + relativedelta(days=2)).strftime('%Y-%m-%d')
end = (datetime.date.today() + relativedelta(days=1)).strftime('%Y-%m-%d 17:00:00')
first_phase = self.browse(cr, uid, ref('project_phase_firstphase0_case1'))
assert (first_phase.date_start == start and first_phase.date_end == end),'Dates are wrong!'
assert (first_phase.date_start[:10] == start),'Start dates are wrong: %s != %s !' % (first_phase.date_start, start)
assert (first_phase.date_end >= end),'End dates are wrong: %s <%s!' % (first_phase.date_end, end)
second_phase = self.browse(cr, uid, ref('project_phase_secondphase0_case2'))
start = first_phase.date_end
end = (datetime.date.today() + relativedelta(days=5)).strftime('%Y-%m-%d')
assert (second_phase.date_start == start and second_phase.date_end == end),'Dates are wrong!'
assert (second_phase.date_start >= start and second_phase.date_end >= end),'Dates are wrong on second phase!'
@@ -147,7 +147,7 @@
I create a phase 'Initial Phase' for project 'Development and Testing'.
-
!record {model: project.phase, id: project_phase_phase0}:
date_start: '2011-01-03'
date_start: '2011-01-03 08:00:00'
duration: 0.0
name: Initial Phase
product_uom: product.uom_day
@@ -38,7 +38,7 @@
- res_users_user0
- res_users_user1
phase_ids:
- date_start: '2011-01-06'
- date_start: '2011-01-06 08:00:00'
duration: 2.0
name: Phase A
product_uom: product.uom_day
@@ -184,16 +184,11 @@
Now I create one phase for the project.
-
!record {model: project.phase, id: project_phase_phasea0}:
date_start: '2011-01-06'
date_start: '2011-01-06 08:00:00'
duration: 2.0
name: Phase A
product_uom: product.uom_day
project_id: project_project_projecta0
resource_ids:
- project_id: project_project_projecta0
resource_id: resource_resource_user0
- project_id: project_project_projecta0
resource_id: resource_resource_user1
state: draft
task_ids:
- delay_hours: 0.0
@@ -21,6 +21,5 @@
import project_compute_phases
import project_compute_tasks
import project_schedule_tasks
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -25,21 +25,19 @@ class project_compute_phases(osv.osv_memory):
_name = 'project.compute.phases'
_description = 'Project Compute Phases'
_columns = {
'target_project': fields.selection([('all', 'Compute All Projects'),
('one', 'Compute a Single Project'),
], 'Schedule', required = True),
'target_project': fields.selection([
('all', 'Compute All My Projects'),
('one', 'Compute a Single Project'),
], 'Schedule', required=True),
'project_id': fields.many2one('project.project', 'Project')
}
_defaults = {
'target_project': 'all'
'target_project': 'one'
}
def check_selection(self, cr, uid, ids, context=None):
return self.compute_date(cr, uid, ids, context=context)
def compute_date(self, cr, uid, ids, context=None):
"""
Compute the phases for scheduling.
@@ -47,12 +45,12 @@ class project_compute_phases(osv.osv_memory):
project_pool = self.pool.get('project.project')
data = self.read(cr, uid, ids, [], context=context)[0]
if not data['project_id'] and data['target_project'] == 'one':
raise osv.except_osv(_('Error!'), _('Please Specify Project to be schedule'))
raise osv.except_osv(_('Error!'), _('Please specify a project to schedule.'))
if data['project_id']: # If project mentioned find its phases
if data['target_project'] == 'one':
project_ids = [data['project_id'][0]]
else: # Else take all the draft,open,pending states phases
project_ids = project_pool.search(cr, uid, [], context=context)
else:
project_ids = project_pool.search(cr, uid, [('user_id','=',uid)], context=context)
project_pool.schedule_phases(cr, uid, project_ids, context=context)
return self._open_phases_list(cr, uid, data, context=context)
@@ -70,8 +68,7 @@ class project_compute_phases(osv.osv_memory):
result = act_obj.read(cr, uid, [id], context=context)[0]
result['target'] = 'current'
project_id = data.get('project_id') and data.get('project_id')[0] or False
result['context'] = {"search_default_project_id":project_id, "default_project_id":project_id, "search_default_responsible_id":uid, "search_default_current": 1}
result['context'] = {"search_default_project_id":project_id, "default_project_id":project_id, "search_default_current": 1}
return result
project_compute_phases()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -2,42 +2,42 @@
<openerp>
<data>
<record id="view_project_compute_phases_select" model="ir.ui.view">
<field name="name">Compute Scheduling of Phases</field>
<record id="view_project_compute_phases_select" model="ir.ui.view">
<field name="name">Schedule Phases</field>
<field name="model">project.compute.phases</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Compute Scheduling of Phases">
<form string="Schedule Phases">
<group colspan="4" col="4">
<separator colspan="4" string="Compute Scheduling of phases for all or specified project" />
<separator colspan="4" string="Schedule Phases" />
<field name="target_project" colspan="4"/>
<field name="project_id" widget="selection" colspan="4" attrs="{'invisible':[('target_project','=','all')], 'required':[('target_project','!=','all')]}"/>
<field name="project_id" colspan="4" attrs="{'invisible':[('target_project','=','all')], 'required':[('target_project','!=','all')]}"/>
<separator colspan="4"/>
<group colspan="2" col="2">
</group>
<group colspan="2" col="2"> <!-- Improve me -->
<button icon="gtk-cancel" special="cancel" string="_Cancel"/>
<button icon="gtk-ok" name="check_selection" string="C_ompute" type="object"/>
<button icon="gtk-cancel" special="cancel" string="_Cancel"/>
<button icon="gtk-ok" name="check_selection" string="C_ompute" type="object"/>
</group>
</group>
</form>
</group>
</form>
</field>
</record>
</record>
<record id="action_project_compute_phases" model="ir.actions.act_window">
<field name="name">Compute Phase Scheduling</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">project.compute.phases</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_project_compute_phases_select"/>
<field name="target">new</field>
<field name="help">To schedule phases of all or a specified project. It then open a gantt view.
</field>
</record>
<record id="action_project_compute_phases" model="ir.actions.act_window">
<field name="name">Schedule Phases</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">project.compute.phases</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_project_compute_phases_select"/>
<field name="target">new</field>
<field name="help">To schedule phases of all or a specified project. It then open a gantt view.
</field>
</record>
<menuitem id="menu_compute_phase" name="Compute Phase Scheduling"
parent="menu_phase_schedule" action="action_project_compute_phases"/>
<menuitem id="menu_compute_phase"
parent="menu_phase_schedule" action="action_project_compute_phases"/>
</data>
</openerp>
@@ -30,7 +30,7 @@ class project_compute_tasks(osv.osv_memory):
def compute_date(self, cr, uid, ids, context=None):
"""
Schedule the tasks according to resource available and priority.
Schedule the tasks according to users and priority.
"""
project_pool = self.pool.get('project.project')
task_pool = self.pool.get('project.task')
@@ -2,39 +2,38 @@
<openerp>
<data>
<record id="view_project_compute_tasks" model="ir.ui.view">
<field name="name">Compute Task Scheduling</field>
<record id="view_project_compute_tasks" model="ir.ui.view">
<field name="name">Schedule Tasks</field>
<field name="model">project.compute.tasks</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Compute Scheduling of Task">
<group colspan="4">
<separator colspan="4" string="Compute Scheduling of Task for specified project." />
<field name="project_id" widget="selection" colspan="4"/>
<form string="Schedule Tasks">
<group colspan="4">
<field name="project_id" colspan="4"/>
<separator colspan="4"/>
<group colspan="2" col="2">
</group>
<group colspan="2" col="2">
<button icon="gtk-cancel" special="cancel" string="_Cancel"/>
<button icon="gtk-ok" name="compute_date" string="C_ompute" type="object"/>
</group>
</group>
</form>
<button icon="gtk-ok" name="compute_date" string="C_ompute" type="object"/>
</group>
</group>
</form>
</field>
</record>
</record>
<record id="action_project_compute_tasks" model="ir.actions.act_window">
<field name="name">Compute Task Scheduling</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">project.compute.tasks</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_project_compute_tasks"/>
<field name="target">new</field>
</record>
<record id="action_project_compute_tasks" model="ir.actions.act_window">
<field name="name">Schedule Tasks</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">project.compute.tasks</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_project_compute_tasks"/>
<field name="target">new</field>
</record>
<menuitem id="menu_compute_tasks" name="Compute Task Scheduling"
parent="menu_phase_schedule" action="action_project_compute_tasks"/>
<menuitem id="menu_compute_tasks"
parent="menu_phase_schedule" action="action_project_compute_tasks"/>
</data>
</openerp>
@@ -1,51 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
class project_schedule_task(osv.osv_memory):
_name = "project.schedule.tasks"
_description = 'project.schedule.tasks'
_columns = {
'msg': fields.char('Message', size=64)
}
_defaults = {
'msg': 'Task Scheduling Completed Successfully'
}
def default_get(self, cr, uid, fields_list, context=None):
res = super(project_schedule_task, self).default_get(cr, uid, fields_list, context)
self.compute_date(cr, uid, context=context)
return res
def compute_date(self, cr, uid, context=None):
"""
Schedule the tasks according to resource available and priority.
"""
phase_pool = self.pool.get('project.phase')
if context is None:
context = {}
if not 'active_id' in context:
return {'type': 'ir.actions.act_window_close'}
return phase_pool.schedule_tasks(cr, uid, [context['active_id']], context=context)
project_schedule_task()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_project_schedule_tasks" model="ir.ui.view">
<field name="name">Schedule Tasks</field>
<field name="model">project.schedule.tasks</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Schedule Tasks">
<group height="90" width="275">
<label string="Task Scheduling completed successfully."/>
<group colspan="4" col="6">
<button icon="gtk-ok" colspan="4" special="cancel" string="_Ok"/>
</group>
</group>
</form>
</field>
</record>
<record id="action_project_schedule_tasks" model="ir.actions.act_window">
<field name="name">Schedule Tasks</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">project.schedule.tasks</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_project_schedule_tasks"/>
<field name="context">{'record_id': active_id}</field>
<field name="target">new</field>
</record>
</data>
</openerp>
+2 -2
View File
@@ -246,7 +246,7 @@
<separator orientation="vertical"/>
<filter icon="terp-emblem-important" name="exception" string="Exception" domain="[('state','in',('except_invoice','except_picking'))]" help="Purchase order which are in the exception state"/>
<separator orientation="vertical"/>
<filter icon="terp-gtk-go-back-rtl" string="Not invoices" domain="[('invoice_ids','=', False)]" help="Purchase orders that include lines not invoiced." groups="base.group_extended"/>
<filter icon="terp-gtk-go-back-rtl" name="not_invoiced" string="Not invoiced" domain="[('invoice_ids','=', False)]" help="Purchase orders that include lines not invoiced." groups="base.group_extended"/>
<separator orientation="vertical"/>
<field name="name" string="Reference"/>
<field name="partner_id"/>
@@ -282,7 +282,7 @@
<separator orientation="vertical"/>
<filter icon="terp-emblem-important" name="exception" string="Exception" domain="[('state','in',('except_invoice','except_picking'))]" help="Purchase orders which are in exception state"/>
<separator orientation="vertical"/>
<filter icon="terp-gtk-go-back-rtl" string="Not invoiced" domain="[('invoice_ids','=', False)]" help="Purchase orders that include lines not invoiced." groups="base.group_extended"/>
<filter icon="terp-gtk-go-back-rtl" name="not_invoiced" string="Not invoiced" domain="[('invoice_ids','=', False)]" help="Purchase orders that include lines not invoiced." groups="base.group_extended"/>
<separator orientation="vertical"/>
<field name="name" string="Reference"/>
<field name="partner_id"/>
@@ -21,7 +21,7 @@
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.view_purchase_order_filter"/>
<field name="arch" type="xml">
<xpath expr="/search/group/filter[@string='Not invoices']" position="after">
<xpath expr="/search/group/filter[@name='not_invoiced']" position="after">
<separator orientation="vertical"/>
<filter icon="terp-gtk-jump-to-rtl" string="Requisition" domain="[('requisition_id','!=',False)]" help="Purchase Orders with requisition"/>
</xpath>
@@ -34,7 +34,7 @@
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.view_request_for_quotation_filter"/>
<field name="arch" type="xml">
<xpath expr="/search/group/filter[@string='Not invoices']" position="after">
<xpath expr="/search/group/filter[@name='not_invoiced']" position="after">
<separator orientation="vertical"/>
<filter icon="terp-gtk-jump-to-rtl" string="Requisition" domain="[('requisition_id','!=',False)]" help="Purchase Orders with requisition"/>
</xpath>
+10 -18
View File
@@ -265,28 +265,20 @@ class resource_resource(osv.osv):
resource_objs = {}
user_pool = self.pool.get('res.users')
for user in user_pool.browse(cr, uid, user_ids, context=context):
resource_objs[user.id] = {
'name' : user.name,
'vacation': [],
'efficiency': 1.0,
}
resource_ids = self.search(cr, uid, [('user_id', '=', user.id)], context=context)
#assert len(resource_ids) < 1, "User should not has more than one resources"
leaves = []
resource_eff = 1.0
if resource_ids:
for resource in self.browse(cr, uid, resource_ids, context=context):
resource_eff = resource.time_efficiency
resource_objs[user.id]['efficiency'] = resource.time_efficiency
resource_cal = resource.calendar_id.id
if resource_cal:
leaves = self.compute_vacation(cr, uid, calendar_id, resource.id, resource_cal, context=context)
temp = {
'name' : resource.name,
'vacation': tuple(leaves),
'efficiency': resource_eff,
}
resource_objs[resource.id] = temp
# resource_objs.append(classobj(str(user.name), (Resource,),{
# '__doc__': user.name,
# '__name__': user.name,
# 'vacation': tuple(leaves),
# 'efficiency': resource_eff,
# }))
resource_objs[user.id]['vacation'] += list(leaves)
return resource_objs
def compute_vacation(self, cr, uid, calendar_id, resource_id=False, resource_calendar=False, context=None):
@@ -323,8 +315,8 @@ class resource_resource(osv.osv):
"""
if not calendar_id:
# Calendar is not specified: working days: 24/7
return [('fri', '1:0-12:0','12:0-24:0'), ('thu', '1:0-12:0','12:0-24:0'), ('wed', '1:0-12:0','12:0-24:0'),
('mon', '1:0-12:0','12:0-24:0'), ('tue', '1:0-12:0','12:0-24:0'), ('sat', '1:0-12:0','12:0-24:0'), ('sun', '1:0-12:0','12:0-24:0')]
return [('fri', '8:0-12:0','13:0-17:0'), ('thu', '8:0-12:0','13:0-17:0'), ('wed', '8:0-12:0','13:0-17:0'),
('mon', '8:0-12:0','13:0-17:0'), ('tue', '8:0-12:0','13:0-17:0')]
resource_attendance_pool = self.pool.get('resource.calendar.attendance')
time_range = "8:00-8:00"
non_working = ""
+2 -2
View File
@@ -47,10 +47,10 @@ class res_partner(osv.osv):
'sale_journal.invoice.type',
type = 'many2one',
relation = 'sale_journal.invoice.type',
string = "Invoicing Method",
string = "Invoicing Type",
view_load = True,
group_name = "Accounting Properties",
help = "The type of journal used for sales and picking."),
help = "This invoicing type will be used, by default, for invoicing the current partner."),
}
res_partner()
+4 -1
View File
@@ -3,6 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2010-2011 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
@@ -22,7 +23,7 @@
{
"name" : "Web Share",
"version" : "1.5",
"version" : "2.0",
"depends" : ["base", "mail"],
"author" : "OpenERP SA",
"category": 'Hidden',
@@ -53,6 +54,8 @@ synchronization with other companies, etc.
'installable': True,
'web': True,
'certificate' : '001301246528927038493',
'js': ['static/src/js/share.js'],
'css': ['static/src/css/share.css'],
'images': ['images/share_wizard.jpeg','images/sharing_wizard_step1.jpeg', 'images/sharing_wizard_step2.jpeg'],
}
+6 -6
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: 2011-01-11 11:16+0000\n"
"PO-Revision-Date: 2011-06-13 06:18+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"PO-Revision-Date: 2011-10-25 12:21+0000\n"
"Last-Translator: Jussi Mikkola <Unknown>\n"
"Language-Team: Finnish <fi@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: 2011-10-19 05:41+0000\n"
"X-Generator: Launchpad (build 14157)\n"
"X-Launchpad-Export-Date: 2011-10-26 05:13+0000\n"
"X-Generator: Launchpad (build 14189)\n"
#. module: share
#: code:addons/share/web/editors.py:15
@@ -149,7 +149,7 @@ msgstr "Jako velho: vaihe 2"
#. module: share
#: view:share.wizard:0
msgid "Share wizard: step 0"
msgstr "JJako velho: vaihe 0"
msgstr ""
#. module: share
#: view:share.wizard:0
@@ -183,7 +183,7 @@ msgstr "Valmis"
#: field:share.wizard,user_ids:0
#: field:share.wizard.user,user_id:0
msgid "Users"
msgstr ""
msgstr "Käyttäjät"
#. module: share
#: code:addons/share/wizard/share_wizard.py:103
+13
View File
@@ -0,0 +1,13 @@
.openerp li.oe-share {
background: url(../img/share.png) no-repeat;
padding-left: 20px;
}
.openerp a.oe-share {
background: url(../img/share.png) no-repeat;
display: block;
float: left;
width: 20px;
height: 20px;
}
+83
View File
@@ -0,0 +1,83 @@
openerp.share = function(instance) {
var QWeb = instance.web.qweb;
QWeb.add_template('/share/static/src/xml/share.xml');
function launch_wizard(self, view) {
var action = view.widget_parent.action;
var Share = new instance.web.DataSet(self, 'share.wizard', view.dataset.get_context());
var domain = view.dataset.domain;
if (view.fields_view.type == 'form') {
domain = new instance.web.CompoundDomain(domain, [['id', '=', view.datarecord.id]]);
}
Share.create({
name: action.name,
domain: domain,
action_id: action.id,
}, function(result) {
var share_id = result.result;
var step1 = Share.call('go_step_1', [[share_id],], function(result) {
var action = result;
self.do_action(action);
});
});
}
var _has_share = null;
function if_has_share(yes, no) {
if (!_has_share) {
_has_share = $.Deferred(function() {
var self = this;
var session = instance.webclient.session;
session.on_session_invalid.add_last(function() { _has_share = null; });
var func = new instance.web.Model(session, "share.wizard").get_func("has_share");
func(session.uid).pipe(function(res) {
if(res) {
self.resolve();
} else {
self.reject();
}
});
});
}
_has_share.done(yes).fail(no);
}
instance.web.Sidebar = instance.web.Sidebar.extend({
add_default_sections: function() {
this._super();
var self = this;
if_has_share(function() {
self.add_items('other', [{
label: 'Share',
callback: self.on_sidebar_click_share,
classname: 'oe-share',
}]);
});
},
on_sidebar_click_share: function(item) {
var view = this.widget_parent
launch_wizard(this, view);
},
});
instance.web.ViewManagerAction.include({
start: function() {
var self = this;
if_has_share(function() {
self.$element.find('a.oe-share').click(self.on_click_share);
}, function() {
self.$element.find('a.oe-share').remove();
});
return this._super.apply(this, arguments);
},
on_click_share: function(e) {
e.preventDefault();
launch_wizard(this, this.views[this.active_view].controller);
},
});
};
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- vim:fdl=1:
-->
<templates id="template" xml:space="preserve">
<t t-extend="ViewManagerAction">
<t t-jquery=".oe-shortcut-toggle" t-operation="after">
<a class="oe-share" href="#share" title="Share this in 2 clicks...">
<!--<img src="/share/static/src/img/share.png"/>-->
</a>
</t>
</t>
</templates>
-2
View File
@@ -1,2 +0,0 @@
import controllers
import editors
-49
View File
@@ -1,49 +0,0 @@
import urlparse
import cherrypy
from openobject import rpc
from openobject.tools import expose, ast
import openerp.controllers
class ShareWizardController(openerp.controllers.SecuredController):
_cp_path = "/share"
@expose()
def index(self, domain, context, view_id, search_domain='[]', action_id=None):
context = ast.literal_eval(context)
if not action_id:
# This should not be needed anymore, but just in case users are
# running the module with an older version of the web client...
# to remove soon-ish
action_id = rpc.RPCProxy('ir.actions.act_window').search(
[('view_id','=',int(view_id))], context=context)
if not action_id: return ""
action_id = action_id[0]
domain = ast.literal_eval(domain)
domain.extend(ast.literal_eval(search_domain))
scheme, netloc, _, _, _ = urlparse.urlsplit(cherrypy.request.base)
share_root_url = urlparse.urlunsplit((
scheme, netloc, '/openerp/login',
'db=%(dbname)s&user=%(login)s&password=%(password)s', ''))
context.update(
#active_ids=share_wiz_id,
#active_id=share_wiz_id[0],
_terp_view_name='Share Wizard',
share_root_url=share_root_url)
Share = rpc.RPCProxy('share.wizard')
sharing_view_id = Share.create({
'domain': str(domain),
'action_id': action_id and int(action_id)
}, context)
return openerp.controllers.actions.execute(
Share.go_step_1([sharing_view_id], context),
ids=[sharing_view_id], context=context)
-94
View File
@@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
import openobject.templating
class FormEditor(openobject.templating.TemplateEditor):
templates = ['/openerp/controllers/templates/form.mako']
MAIN_FORM_BODY = u'id="main_form_body"'
def insert_share_button(self, output):
# Insert the share button on the form title H1 line, at the very end,
# but only if the user is a member of the sharing group
share_opener_insertion = output.index('% endif', output.index(
'/view_diagram/process',
output.index(self.MAIN_FORM_BODY))) + len('% endif')
return output[:share_opener_insertion] + '''
<%
if 'has_share' not in cp.session:
cp.session['has_share'] = rpc.RPCProxy('share.wizard').has_share()
%>
% if cp.session['has_share'] and buttons.toolbar and not is_dashboard:
<a id="share-opener" href="#share" title="${_('Share this in 2 clicks...')}">
<img id="share-opener-img" src="/share/static/images/share.png"/>
</a>
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery("#share-opener").click(function() {
jQuery(this).attr(
"href",
openobject.http.getURL('/share', {
context: jQuery("#_terp_context").val(),
domain: jQuery("#_terp_domain").val(),
view_id: jQuery("#_terp_view_id").val(),
action_id: jQuery("#_terp_action_id").val(),
search_domain: jQuery("#_terp_view_type").val() == "form" ?
("[('id','=',"+jQuery("#_terp_id").val()+")]") :
jQuery("#_terp_search_domain").val(),
}));
});
});
</script>
\n
% endif
''' + output[share_opener_insertion:]
def edit(self, template, template_text):
return self.insert_share_button(
super(FormEditor, self).edit(template, template_text))
class SidebarEditor(openobject.templating.TemplateEditor):
templates = ['/openerp/widgets/templates/sidebar.mako']
ADD_SHARE_SECTION = u'id="sidebar"'
def insert_share_link(self, output):
# Insert the link on the line, right after the link to open the
# attachment form, but only if the user is a member of the sharing group
share_opener_insertion = output.index(
'\n',
output.index(self.ADD_SHARE_SECTION)) + 1
return output[:share_opener_insertion] + '''
<%
if 'has_share' not in cp.session:
cp.session['has_share'] = rpc.RPCProxy('share.wizard').has_share()
%>
% if cp.session['has_share']:
<div id="share-wizard" class="sideheader-a"><h2>${_("Sharing")}</h2></div>
<ul class="clean-a">
<li>
<a id="sharing" href="#share">${_("Share")}</a>
</li>
</ul>
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery("#sharing").click(function() {
jQuery(this).attr(
"href",
openobject.http.getURL('/share', {
context: jQuery("#_terp_context").val(),
domain: jQuery("#_terp_domain").val(),
view_id: jQuery("#_terp_view_id").val(),
action_id: jQuery("#_terp_action_id").val(),
search_domain: jQuery("#_terp_view_type").val() == "form" ?
("[('id','=',"+jQuery("#_terp_id").val()+")]") :
jQuery("#_terp_search_domain").val(),
}));
});
});
</script>
\n
% endif
''' + output[share_opener_insertion:]
def edit(self, template, template_text):
return self.insert_share_link(
super(SidebarEditor, self).edit(template, template_text))
+29 -29
View File
@@ -119,24 +119,38 @@ class share_wizard(osv.osv_memory):
values['name'] = action.name
return super(share_wizard,self).create(cr, uid, values, context=context)
def share_url_template(self, cr, uid, _ids, context=None):
# NOTE: take _ids in parameter to allow usage through browse_record objects
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='', context=context)
if base_url:
base_url += '/?db=%(dbname)s&login=%(login)s'
return base_url
def _share_root_url(self, cr, uid, ids, _fieldname, _args, context=None):
result = dict.fromkeys(ids, '')
data = dict(dbname=cr.dbname, login='')
for this in self.browse(cr, uid, ids, context=context):
result[this.id] = this.share_url_template() % data
return result
_columns = {
'action_id': fields.many2one('ir.actions.act_window', 'Action to share', required=True,
help="The action that opens the screen containing the data you wish to share."),
'domain': fields.char('Domain', size=256, help="Optional domain for further data filtering"),
'user_type': fields.selection(_user_type_selection,'Users to share with',
'user_type': fields.selection(lambda s, *a, **k: s._user_type_selection(*a, **k),'Users to share with', required=True,
help="Select the type of user(s) you would like to share data with."),
'new_users': fields.text("Emails"),
'access_mode': fields.selection([('readonly','Can view'),('readwrite','Can edit')],'Access Mode', required=True,
help="Access rights to be granted on the shared documents."),
'result_line_ids': fields.one2many('share.wizard.result.line', 'share_wizard_id', 'Summary', readonly=True),
'share_root_url': fields.char('Share Access URL', size=512, readonly=True, tooltip='Main access page for users that are granted shared access'),
'share_root_url': fields.function(_share_root_url, string='Share Access URL', type='char', size=512, readonly=True,
help='Main access page for users that are granted shared access'),
'name': fields.char('Share Title', size=64, required=True, help="Title for the share (displayed to users as menu and shortcut name)"),
'message': fields.text("Personal Message", help="An optional personal message, to be included in the e-mail notification."),
}
_defaults = {
'user_type' : 'new',
'user_type' : 'emails',
'domain': lambda self, cr, uid, context, *a: context.get('domain', '[]'),
'share_root_url': lambda self, cr, uid, context, *a: context.get('share_root_url') or _('Please specify "share_root_url" in context'),
'action_id': lambda self, cr, uid, context, *a: context.get('action_id'),
'access_mode': 'readonly',
}
@@ -537,12 +551,11 @@ class share_wizard(osv.osv_memory):
try:
domain = safe_eval(wizard_data.domain)
if domain:
domain_expr = expression.expression(domain)
for rel_field, model in fields_relations:
related_domain = []
if not rel_field: continue
for element in domain:
if domain_expr._is_leaf(element):
if expression.is_leaf(element):
left, operator, right = element
left = '%s.%s'%(rel_field, left)
element = left, operator, right
@@ -555,19 +568,6 @@ class share_wizard(osv.osv_memory):
raise osv.except_osv(_('Sharing access could not be created'),
_('Sorry, the current screen and filter you are trying to share are not supported at the moment.\nYou may want to try a simpler filter.'))
def _finish_result_lines(self, cr, uid, wizard_data, share_group_id, context=None):
"""Perform finalization of the share, and populate the summary"""
share_root_url = wizard_data.share_root_url
format_url = '%(dbname)s' in share_root_url
for result in wizard_data.result_line_ids:
# share_root_url may contain placeholders for dbname, login and password
share_url = (share_root_url % \
{'login': quote_plus(result.user_id.login),
'password': '', # kept empty for security reasons
'dbname': quote_plus(cr.dbname)}) \
if format_url else share_root_url
result.write({'share_url': share_url}, context=context)
def _check_preconditions(self, cr, uid, wizard_data, context=None):
self._assert(wizard_data.action_id and wizard_data.access_mode,
_('Action and Access Mode are required to create a shared access'),
@@ -662,15 +662,6 @@ class share_wizard(osv.osv_memory):
# C.
self._create_indirect_sharing_rules(cr, current_user, wizard_data, group_id, obj1, context=context)
# so far, so good -> check summary results and return them
self._finish_result_lines(cr, uid, wizard_data, group_id, context=context)
# update share URL to make it generic
generic_share_url = wizard_data.share_root_url % {'dbname': cr.dbname,
'login': '',
'password': ''}
wizard_data.write({'share_root_url': generic_share_url}, context=context)
# refresh wizard_data
wizard_data = self.browse(cr, uid, ids[0], context=context)
@@ -742,11 +733,20 @@ share_wizard()
class share_result_line(osv.osv_memory):
_name = 'share.wizard.result.line'
_rec_name = 'user_id'
def _share_url(self, cr, uid, ids, _fieldname, _args, context=None):
result = dict.fromkeys(ids, '')
for this in self.browse(cr, uid, ids, context=context):
data = dict(dbname=cr.dbname, login=this.login)
result[this.id] = this.share_wizard_id.share_url_template() % data
return result
_columns = {
'user_id': fields.many2one('res.users', required=True, readonly=True),
'login': fields.related('user_id', 'login', string='Login', type='char', size=64, required=True, readonly=True),
'password': fields.char('Password', size=64, readonly=True),
'share_url': fields.char('Share URL', size=512, required=True),
'share_url': fields.function(_share_url, string='Share URL', type='char', size=512),
'share_wizard_id': fields.many2one('share.wizard', 'Share Wizard', required=True),
'newly_created': fields.boolean('Newly created', readonly=True),
}
+4 -2
View File
@@ -29,6 +29,10 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Grant instant access to your documents">
<group name="groups_group" colspan="4" col="4">
<separator colspan="4" string="Who do you want to share with?"/>
<field name="user_type" />
</group>
<group colspan="4" name="emails_group" attrs="{'invisible':[('user_type','!=','emails')]}">
<separator colspan="4" string="Share with these people (one e-mail per line)"/>
<field colspan="4" nolabel="1" name="new_users" attrs="{'required':[('user_type','=','emails')]}"/>
@@ -78,7 +82,6 @@
- 'action_id' (id of action)
- 'domain' (string expression for full domain to apply as sent to server,
with dynamic data like 'uid' replaced by actual value (i.e. after eval)!)
- 'share_root_url' : URL for direct access to share page (may include %(login)s and %(password)s placeholders)
-->
<record id="action_share_wizard" model="ir.actions.act_window">
<field name="name">Share Wizard</field>
@@ -94,7 +97,6 @@
- 'action_id' (id of action)
- 'domain' (string expression for full domain to apply as sent to server,
with dynamic data like 'uid' replaced by actual value (i.e. after eval)!)
- 'share_root_url' : URL for direct access to share page (may include %(login)s and %(password)s placeholders)
-->
<record id="action_share_wizard_step1" model="ir.actions.act_window">
<field name="name">Share Wizard</field>
+14 -20
View File
@@ -127,39 +127,33 @@
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban default_group_by="type">
<field name="color"/>
<field name="type"/>
<field name="product_image"/>
<field name="list_price"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value || record.type.raw_value)"/>
<div t-att-class="color + (record.color.raw_value == 1 ? ' oe_kanban_color_alert' : '')">
<div class="oe_kanban_box oe_kanban_color_border">
<table class="oe_kanban_table oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle">
<tr>
<td class="oe_kanban_title1" align="left" valign="middle">
<t t-if="record.default_code.raw_value">[<field name="default_code"/>]</t>
<field name="name"/>
</td>
<td align="right" valign="top" width="22">
<img t-att-src="kanban_gravatar(record.product_manager.raw_value, 22)" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
<div class="oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle oe_kanban_title1">
<field name="name"/>
</div>
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger">
<table class="oe_kanban_table">
<tr>
<td valign="top" width="22">
<t t-if="record.product_image.raw_value">
<img t-att-src="'data:image/png;base64,'+record.product_image.raw_value" width='48' height='48'/>
</t>
<t t-if="!record.product_image.raw_value">
<img src="/web/static/src/img/product_default.png"/>
</t>
<td valign="top" width="48">
<img t-att-src="kanban_image('product.product', 'product_image', record.id.value)" width="48"/>
</td>
<td>
<div class="oe_kanban_title2">
<t t-if="record.type.raw_value=='service'">No Stock</t>
<t t-if="record.type.raw_value!='service'">
<div><field name="qty_available"/> On Hand , <field name="virtual_available"/> Available</div>
<div><t t-if="record.list_price.raw_value!=0">Public Price : <field name="lst_price"/> ,</t> Cost : <field name="standard_price"/></div>
<div>
<t t-if="record.list_price.raw_value!=0">Public Price : <field name="lst_price"/> ,</t>
Cost : <field name="standard_price"/>
</div>
</t>
</div>
</td>
@@ -170,7 +164,7 @@
<div class="oe_kanban_left">
<a string="Edit" icon="gtk-edit" type="edit"/>
<a string="Change Color" icon="color-picker" type="color" name="color"/>
<a string="Update Stock" name="%(stock.action_view_change_product_quantity)d" icon="gtk-execute" type="action"/>
<a string="Update Stock" name="%(stock.action_view_change_product_quantity)d" icon="gtk-execute" type="action"/>
</div>
<br class="oe_kanban_clear"/>
</div>
+594
View File
@@ -0,0 +1,594 @@
# Spanish (Chile) translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-01-11 11:16+0000\n"
"PO-Revision-Date: 2011-10-24 19:30+0000\n"
"Last-Translator: Francisco Reyes Acuña <Unknown>\n"
"Language-Team: Spanish (Chile) <es_CL@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: 2011-10-25 04:53+0000\n"
"X-Generator: Launchpad (build 14185)\n"
#. module: stock_location
#: selection:product.pulled.flow,picking_type:0
#: selection:stock.location.path,picking_type:0
msgid "Sending Goods"
msgstr "Envío de mercancías"
#. module: stock_location
#: view:product.product:0
msgid "Pulled Paths"
msgstr "Rutas arrastradas"
#. module: stock_location
#: selection:product.pulled.flow,type_proc:0
msgid "Move"
msgstr "Movimiento"
#. module: stock_location
#: model:ir.model,name:stock_location.model_stock_location_path
msgid "Pushed Flows"
msgstr "Flujos empujados"
#. module: stock_location
#: selection:stock.location.path,auto:0
msgid "Automatic No Step Added"
msgstr "Movimiento automático, paso no añadido"
#. module: stock_location
#: view:product.product:0
msgid "Parameters"
msgstr "Parámetros"
#. module: stock_location
#: field:stock.location.path,location_from_id:0
msgid "Source Location"
msgstr "Ubicación de origen"
#. module: stock_location
#: help:product.pulled.flow,cancel_cascade:0
msgid "Allow you to cancel moves related to the product pull flow"
msgstr ""
"Le permite cancelar movimientos relacionados con el flujo de arrastre de "
"producto."
#. module: stock_location
#: model:ir.model,name:stock_location.model_product_pulled_flow
#: field:product.product,flow_pull_ids:0
msgid "Pulled Flows"
msgstr "Flujos arrastrados"
#. module: stock_location
#: constraint:stock.move:0
msgid "You must assign a production lot for this product"
msgstr "Debe asignar un lote de producción para este producto"
#. module: stock_location
#: help:product.pulled.flow,location_src_id:0
msgid "Location used by Destination Location to supply"
msgstr "Ubicación usada como destino al abastecer."
#. module: stock_location
#: selection:product.pulled.flow,picking_type:0
#: selection:stock.location.path,picking_type:0
msgid "Internal"
msgstr "Interno"
#. module: stock_location
#: code:addons/stock_location/procurement_pull.py:98
#, python-format
msgid ""
"Pulled procurement coming from original location %s, pull rule %s, via "
"original Procurement %s (#%d)"
msgstr ""
"Abastecimiento arrastrado proveniente de la ubicación original %s, regla de "
"arrastre %s, vía abastecimiento original %s (#%d)"
#. module: stock_location
#: model:ir.model,name:stock_location.model_stock_location
msgid "Location"
msgstr "Ubicación"
#. module: stock_location
#: field:product.pulled.flow,invoice_state:0
#: field:stock.location.path,invoice_state:0
msgid "Invoice Status"
msgstr "Estado de facturación"
#. module: stock_location
#: help:product.pulled.flow,name:0
msgid "This field will fill the packing Origin and the name of its moves"
msgstr ""
"Este campo rellenará el origen de la guiá de despacho y el nombre de sus "
"movimientos."
#. module: stock_location
#: help:stock.location.path,auto:0
msgid ""
"This is used to define paths the product has to follow within the location "
"tree.\n"
"The 'Automatic Move' value will create a stock move after the current one "
"that will be validated automatically. With 'Manual Operation', the stock "
"move has to be validated by a worker. With 'Automatic No Step Added', the "
"location is replaced in the original move."
msgstr ""
"Se utiliza para definir rutas que el producto debe seguir dentro del árbol "
"de ubicaciones.\n"
"La opción 'Movimiento automático' creará un movimiento de stock después del "
"actual que se validará automáticamente. Con 'Operación manual', el "
"movimiento de stock debe ser validado por un trabajador. Con 'Automático "
"paso no añadido', la ubicación se reemplaza en el movimiento original."
#. module: stock_location
#: model:ir.module.module,shortdesc:stock_location.module_meta_information
msgid "Warehouse Locations Paths"
msgstr "Rutas en las ubicaciones de almacén"
#. module: stock_location
#: view:product.product:0
msgid "Conditions"
msgstr "Condiciones"
#. module: stock_location
#: model:stock.location,name:stock_location.location_pack_zone
msgid "Pack Zone"
msgstr "Zona de empaquetado"
#. module: stock_location
#: model:stock.location,name:stock_location.location_gate_b
msgid "Gate B"
msgstr "Puerta B"
#. module: stock_location
#: model:stock.location,name:stock_location.location_gate_a
msgid "Gate A"
msgstr "Puerta A"
#. module: stock_location
#: selection:product.pulled.flow,type_proc:0
msgid "Buy"
msgstr "Comprar"
#. module: stock_location
#: view:product.product:0
msgid "Pushed flows"
msgstr "Flujos empujados"
#. module: stock_location
#: model:stock.location,name:stock_location.location_dispatch_zone
msgid "Dispatch Zone"
msgstr "Zona de despacho"
#. module: stock_location
#: model:ir.model,name:stock_location.model_stock_move
msgid "Stock Move"
msgstr "Movimiento de stock"
#. module: stock_location
#: view:product.product:0
msgid "Pulled flows"
msgstr "Flujos arrastrados"
#. module: stock_location
#: field:product.pulled.flow,company_id:0
#: field:stock.location.path,company_id:0
msgid "Company"
msgstr "Empresa"
#. module: stock_location
#: view:product.product:0
msgid "Logistics Flows"
msgstr "Flujos de logística"
#. module: stock_location
#: help:stock.move,cancel_cascade:0
msgid "If checked, when this move is cancelled, cancel the linked move too"
msgstr ""
"Si está marcado, cuando este movimiento se cancela, también cancela el "
"movimiento relacionado."
#. module: stock_location
#: selection:product.pulled.flow,type_proc:0
msgid "Produce"
msgstr "Producir"
#. module: stock_location
#: selection:product.pulled.flow,procure_method:0
msgid "Make to Order"
msgstr "Obtener bajo pedido"
#. module: stock_location
#: selection:product.pulled.flow,procure_method:0
msgid "Make to Stock"
msgstr "Obtener para stock"
#. module: stock_location
#: field:product.pulled.flow,partner_address_id:0
msgid "Partner Address"
msgstr "Dirección de empresa"
#. module: stock_location
#: selection:product.pulled.flow,invoice_state:0
#: selection:stock.location.path,invoice_state:0
msgid "To Be Invoiced"
msgstr "Para facturar"
#. module: stock_location
#: help:stock.location.path,delay:0
msgid "Number of days to do this transition"
msgstr "Número de días para realizar esta transición"
#. module: stock_location
#: model:ir.module.module,description:stock_location.module_meta_information
msgid ""
"\n"
"This module supplements the Warehouse application by adding support for per-"
"product\n"
"location paths, effectively implementing Push and Pull inventory flows.\n"
"\n"
"Typically this could be used to:\n"
"* Manage product manufacturing chains\n"
"* Manage default locations per product\n"
"* Define routes within your warehouse according to business needs, such as:\n"
" - Quality Control\n"
" - After Sales Services\n"
" - Supplier Returns\n"
"* Help rental management, by generating automated return moves for rented "
"products\n"
"\n"
"Once this module is installed, an additional tab appear on the product form, "
"where you can add\n"
"Push and Pull flow specifications. The demo data of CPU1 product for that "
"push/pull :\n"
"\n"
"Push flows\n"
"----------\n"
"Push flows are useful when the arrival of certain products in a given "
"location should always\n"
"be followed by a corresponding move to another location, optionally after a "
"certain delay.\n"
"The original Warehouse application already supports such Push flow "
"specifications on the\n"
"Locations themselves, but these cannot be refined per-product.\n"
"\n"
"A push flow specification indicates which location is chained with which "
"location, and with\n"
"what parameters. As soon as a given quantity of products is moved in the "
"source location,\n"
"a chained move is automatically foreseen according to the parameters set on "
"the flow specification\n"
"(destination location, delay, type of move, journal, etc.) The new move can "
"be automatically\n"
"processed, or require a manual confirmation, depending on the parameters.\n"
"\n"
"Pull flows\n"
"----------\n"
"Pull flows are a bit different from Pull flows, in the sense that they are "
"not related to\n"
"the processing of product moves, but rather to the processing of procurement "
"orders.\n"
"What is being pulled is a need, not directly products.\n"
"A classical example of Push flow is when you have an Outlet company, with a "
"parent Company\n"
"that is responsible for the supplies of the Outlet.\n"
"\n"
" [ Customer ] <- A - [ Outlet ] <- B - [ Holding ] <~ C ~ [ Supplier ]\n"
"\n"
"When a new procurement order (A, coming from the confirmation of a Sale "
"Order for example) arrives\n"
"in the Outlet, it is converted into another procurement (B, via a Push flow "
"of type 'move')\n"
"requested from the Holding. When procurement order B is processed by the "
"Holding company, and\n"
"if the product is out of stock, it can be converted into a Purchase Order "
"(C) from the Supplier\n"
"(Push flow of type Purchase). The result is that the procurement order, the "
"need, is pushed\n"
"all the way between the Customer and Supplier.\n"
"\n"
"Technically, Pull flows allow to process procurement orders differently, not "
"only depending on\n"
"the product being considered, but also depending on which location holds the "
"\"need\" for that\n"
"product (i.e. the destination location of that procurement order).\n"
"\n"
"Use-Case\n"
"--------\n"
"\n"
"You can use the demo data as follow:\n"
" CPU1: Sell some CPU1 from Shop 1 and run the scheduler\n"
" - Warehouse: delivery order, Shop 1: reception\n"
" CPU3:\n"
" - When receiving the product, it goes to Quality Control location then "
"stored to shelf 2.\n"
" - When delivering the customer: Pick List -> Packing -> Delivery Order "
"from Gate A\n"
" "
msgstr ""
"\n"
"Este módulo complementa la aplicación Almacén, añadiendo soporte para cada "
"producto,\n"
"ruta de ubicación, aplicación efectiva de flujos de inventario de Entrada y "
"Salida.\n"
"\n"
"Normalmente, esto se podría utilizar para:\n"
"* Gestión de las cadenas de fabricación de productos\n"
"* Gestionar ubicaciones predeterminadas por producto\n"
"* Definir las rutas dentro de su almacén de acuerdo a las necesidades "
"empresariales, tales como:\n"
" - Control de Calidad\n"
" - Después de Servicios de Ventas\n"
" - Proveedor Devoluciones\n"
"* Gestión de Ayuda a la rentabilidad, mediante la generación de movimientos "
"automáticos para productos alquilados\n"
"\n"
"Una vez que este módulo está instalado, aparecerá una ficha adicional en la "
"pestaña del producto, donde se puede añadir\n"
"las especificaciones del flujo de Entrada y de Salida. Los datos de "
"demostración del producto CPU1 para esos flujos de entrada/salida:\n"
"\n"
"Flujos de Entrada\n"
"----------\n"
"Los flujos de entrada son útiles cuando la llegada de determinados productos "
"a un lugar determinado siempre\n"
"va seguida de un movimiento que corresponde a otra ubicación, opcionalmente "
"después de un cierto retraso.\n"
"La aplicación Almacén original ya soporta tales especificaciones del flujo "
"de entrada en sus Ubicaciones, pero estas no pueden ser refinadas por "
"producto.\n"
"\n"
"Una especificación de flujo de entrada indica qué ubicación está encadenada "
"con qué ubicación, y con\n"
"qué parámetros. Tan pronto como una cantidad determinada de productos se "
"mueve de la ubicación de origen,\n"
"un movimiento encadenado de forma automática configurado de acuerdo con los "
"parámetros establecidos en la especificación del flujo\n"
"(lugar de destino, demora, tipo de movimiento, diarios, etc) se dispara. El "
"nuevo movimiento puede ser automáticamente\n"
"procesado, o requerir una confirmación manual, dependiendo de los "
"parámetros.\n"
"\n"
"Flujos de Salida\n"
"----------\n"
"Los flujos de salida son un poco diferentes de los flujos de entrada, en el "
"sentido de que no están relacionados con\n"
"la tramitación de movimientos de productos, sino más bien con el tratamiento "
"de los pedidos de venta.\n"
"Lo que se saca es una necesidad, no directamente los productos.\n"
"Un ejemplo clásico de flujo de salida es cuando usted tiene una empresa de "
"Outlet, con una empresa padre\n"
"que es la responsable de los suministros del Outlet.\n"
"\n"
" [cliente] <- A - [Outlet] <- B - [Suministrador] <~ C ~ [Proveedor]\n"
"\n"
"Cuando una nueva orden de compra (A, procedente de la confirmación de una "
"orden de venta por ejemplo) llega al Outlet, se convierte en otra compra (B, "
"a través de un flujo de entrada del tipo 'mover')\n"
"solicitada desde el socio. Cuando el orden de compa para B es procesado por "
"la empresa socia, y\n"
"si el producto está agotado, puede convertirse en una Orden de Compra (C) "
"del Proveedor\n"
"(flujo de Entrada de tipo Compra). El resultado es que el orden de "
"adquisición, la necesidad, se traslada automáticamente del Cliente al "
"Proveedor.\n"
"\n"
"Técnicamente, los flujos de Salida permiten procesar los pedidos de "
"adquisición de otra manera, no sólo en función del producto considerado, "
"sino también en función de qué ubicación tiene la \"necesidad\" de qué\n"
"producto (es decir, la ubicación de destino de esa orden de compra).\n"
"\n"
"Caso de Uso\n"
"---------------\n"
"\n"
"Puede utilizar los datos de demostración de la siguiente manera:\n"
" CPU1: Venta de algunas CPU1 en la tienda 1 y ejecutar el planificador\n"
" - Almacén: órden de entrega, Tienda 1: Recepción\n"
" CPU3:\n"
" - Al recibir el producto, va al Control de Calidad y se almacena en la "
"plataforma 2.\n"
" - Cuando se entrega al cliente: Lista de Selección -> Embalaje -> Orden "
"de Entrega desde la puerta A\n"
" "
#. module: stock_location
#: field:product.pulled.flow,type_proc:0
msgid "Type of Procurement"
msgstr "Tipo de abastecimiento"
#. module: stock_location
#: help:product.pulled.flow,company_id:0
msgid "Is used to know to which company belong packings and moves"
msgstr ""
"Se usa para saber a que compañía pertenece las guias de despacho y "
"movimientos."
#. module: stock_location
#: field:product.pulled.flow,name:0
msgid "Name"
msgstr "Nombre"
#. module: stock_location
#: help:product.product,path_ids:0
msgid ""
"These rules set the right path of the product in the whole location tree."
msgstr ""
"Estas reglas fijan la ruta correcta del producto en todo el árbol de "
"ubicaciones."
#. module: stock_location
#: selection:stock.location.path,auto:0
msgid "Manual Operation"
msgstr "Operación manual"
#. module: stock_location
#: model:ir.model,name:stock_location.model_product_product
#: field:product.pulled.flow,product_id:0
msgid "Product"
msgstr "Producto"
#. module: stock_location
#: field:product.pulled.flow,procure_method:0
msgid "Procure Method"
msgstr "Método abastecimiento"
#. module: stock_location
#: field:product.pulled.flow,picking_type:0
#: field:stock.location.path,picking_type:0
msgid "Shipping Type"
msgstr "Tipo de envío"
#. module: stock_location
#: help:product.pulled.flow,procure_method:0
msgid ""
"'Make to Stock': When needed, take from the stock or wait until re-"
"supplying. 'Make to Order': When needed, purchase or produce for the "
"procurement request."
msgstr ""
"'Obtener para stock': Cuando sea necesario, coger del stock o esperar hasta "
"que sea reabastecido. 'Obtener bajo pedido': Cuando sea necesario, comprar o "
"producir para la petición de abastecimiento."
#. module: stock_location
#: help:product.pulled.flow,location_id:0
msgid "Is the destination location that needs supplying"
msgstr "Es la ubicación destino que necesita suministro."
#. module: stock_location
#: field:stock.location.path,product_id:0
msgid "Products"
msgstr "Productos"
#. module: stock_location
#: code:addons/stock_location/procurement_pull.py:118
#, python-format
msgid "Pulled from another location via procurement %d"
msgstr "Arrastrado desde otra ubicación vía abastecimiento %d"
#. module: stock_location
#: model:stock.location,name:stock_location.stock_location_qualitytest0
msgid "Quality Control"
msgstr "Control de calidad"
#. module: stock_location
#: selection:product.pulled.flow,invoice_state:0
#: selection:stock.location.path,invoice_state:0
msgid "Not Applicable"
msgstr "No Aplicable"
#. module: stock_location
#: field:stock.location.path,delay:0
msgid "Delay (days)"
msgstr "Retraso (días)"
#. module: stock_location
#: code:addons/stock_location/procurement_pull.py:67
#, python-format
msgid ""
"Picking for pulled procurement coming from original location %s, pull rule "
"%s, via original Procurement %s (#%d)"
msgstr ""
"Guía de despacho para abastecimiento arrastrado proveniente de la ubicación "
"original %s, regla de arrastre %s, vía abastecimiento original %s (#%d)"
#. module: stock_location
#: field:product.product,path_ids:0
msgid "Pushed Flow"
msgstr "Flujo empujado"
#. module: stock_location
#: code:addons/stock_location/procurement_pull.py:89
#, python-format
msgid ""
"Move for pulled procurement coming from original location %s, pull rule %s, "
"via original Procurement %s (#%d)"
msgstr ""
"Movimiento para abastecimiento arrastrado proveniente de la ubicación "
"original %s, regla de arrastre %s, vía abastecimiento original %s (#%d)"
#. module: stock_location
#: constraint:stock.move:0
msgid "You try to assign a lot which is not from the same product"
msgstr "Está intentando asignar un lote que no es del mismo producto"
#. module: stock_location
#: model:ir.model,name:stock_location.model_procurement_order
msgid "Procurement"
msgstr "Abastecimiento"
#. module: stock_location
#: field:stock.location.path,location_dest_id:0
msgid "Destination Location"
msgstr "Ubicación de destino"
#. module: stock_location
#: field:stock.location.path,auto:0
#: selection:stock.location.path,auto:0
msgid "Automatic Move"
msgstr "Movimiento automático"
#. module: stock_location
#: selection:product.pulled.flow,picking_type:0
#: selection:stock.location.path,picking_type:0
msgid "Getting Goods"
msgstr "Recepción mercancías"
#. module: stock_location
#: view:product.product:0
msgid "Action Type"
msgstr "Tipo de acción"
#. module: stock_location
#: constraint:product.product:0
msgid "Error: Invalid ean code"
msgstr "Error: Código EAN no válido"
#. module: stock_location
#: help:product.pulled.flow,picking_type:0
#: help:stock.location.path,picking_type:0
msgid ""
"Depending on the company, choose whatever you want to receive or send "
"products"
msgstr "Según la compañía, seleccionar si desea recibir o enviar productos."
#. module: stock_location
#: model:stock.location,name:stock_location.location_order
msgid "Order Processing"
msgstr "Procesando pedido"
#. module: stock_location
#: field:stock.location.path,name:0
msgid "Operation"
msgstr "Operación"
#. module: stock_location
#: view:product.product:0
#: field:product.product,path_ids:0
#: view:stock.location.path:0
msgid "Location Paths"
msgstr "Rutas de ubicaciones"
#. module: stock_location
#: field:product.pulled.flow,journal_id:0
#: field:stock.location.path,journal_id:0
msgid "Journal"
msgstr "Diario"
#. module: stock_location
#: field:product.pulled.flow,cancel_cascade:0
#: field:stock.move,cancel_cascade:0
msgid "Cancel Cascade"
msgstr "Cancelar cascada"
#. module: stock_location
#: selection:product.pulled.flow,invoice_state:0
#: selection:stock.location.path,invoice_state:0
msgid "Invoiced"
msgstr "Facturado"
+83
View File
@@ -0,0 +1,83 @@
# Spanish (Chile) translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-01-03 16:58+0000\n"
"PO-Revision-Date: 2011-10-24 19:35+0000\n"
"Last-Translator: Francisco Reyes Acuña <Unknown>\n"
"Language-Team: Spanish (Chile) <es_CL@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: 2011-10-25 04:53+0000\n"
"X-Generator: Launchpad (build 14185)\n"
#. module: stock_no_autopicking
#: model:ir.model,name:stock_no_autopicking.model_product_product
msgid "Product"
msgstr "Producto"
#. module: stock_no_autopicking
#: model:ir.module.module,description:stock_no_autopicking.module_meta_information
msgid ""
"\n"
" This module allows an intermediate picking process to provide raw "
"materials\n"
" to production orders.\n"
"\n"
" One example of usage of this module is to manage production made by "
"your\n"
" suppliers (sub-contracting). To achieve this, set the assembled product\n"
" which is sub-contracted to \"No Auto-Picking\" and put the location of "
"the\n"
" supplier in the routing of the assembly operation.\n"
" "
msgstr ""
"\n"
" Este módulo permite un proceso de recogida intermedio para proveer "
"materias\n"
" primas a las órdenes de fabricación.\n"
"\n"
" Un ejemplo de uso de este módulo es gestionar la producción realizada "
"por sus\n"
" proveedores (subcontratación). Para conseguir esto, establezca los "
"productos ensamblados\n"
" que serán subcontratados a \"No Auto-Recogida\" y establezca la "
"ubicación del nuevo\n"
" proveedor en el proceso de la operación de ensamblado.\n"
" "
#. module: stock_no_autopicking
#: model:ir.model,name:stock_no_autopicking.model_mrp_production
msgid "Manufacturing Order"
msgstr "Orden de fabricación"
#. module: stock_no_autopicking
#: field:product.product,auto_pick:0
msgid "Auto Picking"
msgstr "Auto empaquetado"
#. module: stock_no_autopicking
#: help:product.product,auto_pick:0
msgid "Auto picking for raw materials of production orders."
msgstr "Auto empaquetado para materiales a granel en órdenes de producción."
#. module: stock_no_autopicking
#: constraint:product.product:0
msgid "Error: Invalid ean code"
msgstr "Error: Código EAN no válido"
#. module: stock_no_autopicking
#: model:ir.module.module,shortdesc:stock_no_autopicking.module_meta_information
msgid "Stock No Auto-Picking"
msgstr "Stock no auto empaquetado"
#. module: stock_no_autopicking
#: constraint:mrp.production:0
msgid "Order quantity cannot be negative or zero !"
msgstr "¡La cantidad ordenada no puede ser negativa o cero!"
File diff suppressed because it is too large Load Diff