').dialog({
+ modal: true,
+ title: 'Import Successful!',
+ buttons: {
+ Ok: function() {
+ $(this).dialog("close");
+ window.location = "/web/webclient/home";
+ }
+ }
+ }).html('The document has been successfully imported!');
+ }
+ },
+ on_imported_error: function(response){
+ var self = this;
+ var msg = "Sorry, the document could not be imported.";
+ if (response.data.fault_code) {
+ msg += "\n Reason:" + response.data.fault_code;
+ }
+ var params = {error: response, message: msg};
+ $(openerp.web.qweb.render("DialogWarning", params)).dialog({
+ title: "Document Import Notification",
+ modal: true,
+ buttons: {
+ Ok: function() { $(this).dialog("close"); }
+ }
+ });
+ }
+});
+
+}
+// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/edi/static/src/xml/edi.xml b/addons/edi/static/src/xml/edi.xml
new file mode 100644
index 00000000000..1e662a2b2cc
--- /dev/null
+++ b/addons/edi/static/src/xml/edi.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/edi/static/src/xml/edi_account.xml b/addons/edi/static/src/xml/edi_account.xml
new file mode 100644
index 00000000000..dc6fb7f9754
--- /dev/null
+++ b/addons/edi/static/src/xml/edi_account.xml
@@ -0,0 +1,163 @@
+
+
+
+
+
+
Invoice :
+
+
+ Description
+ Date
+ Your Reference
+
+
+
+
+
+
+
+
+
+
+ Product Description
+ Quantity
+ Unit Price
+ Discount
+ Price
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Net Total:
+
+
+
+
+
+
+
+
+
+
+ Taxes:
+
+
+
+
+
+
+
+
+
+
+ Total:
+
+
+
+
+
+
+
+
+
+
+ Tax
+ Base Amount
+ Amount
+
+
+
+
+
+
+
+
+
+
+
+
+ Notes:
+
+
+
+
+
+
+
+
+
+
+
+ Paypal
+
+
+ You may directly pay this invoice online via Paypal's secure payment gateway:
+
+
+
+
+
+
+
+
+ Bank Wire Transfer
+
+
+ Please transfer to
+ (postal address on the invoice header)
+ using one of the following bank accounts. Be sure to mention the invoice
+ reference on the transfer:
+
+
+
+
+
+
+
+
diff --git a/addons/edi/static/src/xml/edi_sale_purchase.xml b/addons/edi/static/src/xml/edi_sale_purchase.xml
new file mode 100644
index 00000000000..4e0fdd7fc4e
--- /dev/null
+++ b/addons/edi/static/src/xml/edi_sale_purchase.xml
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
Order :
+
+
+
+ Your Reference
+ Date
+ Salesman
+ Payment terms
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Product Description
+ Quantity
+ Unit Price
+ Discount(%)
+ Price
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Net Total:
+
+
+
+
+
+
+
+
+
+
+ Taxes:
+
+
+
+
+
+
+
+
+
+
+ Total:
+
+
+
+
+
+
+
+
+ Notes:
+
+
+
+
+
+
+
+
+
+
+
+ Paypal
+
+
+ You may directly pay this order online via Paypal's secure payment gateway:
+
+
+
+
+
+
+
+
+ Bank Wire Transfer
+
+
+ Please transfer to
+ (postal address on the order header)
+ using one of the following bank accounts. Be sure to mention the document
+ reference on the transfer:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/edi/test/edi_partner_test.yml b/addons/edi/test/edi_partner_test.yml
new file mode 100644
index 00000000000..cca39417927
--- /dev/null
+++ b/addons/edi/test/edi_partner_test.yml
@@ -0,0 +1,44 @@
+-
+ In order to test the basic EDI system, I will export a partner,
+ modify the exported EDI document to add an attachment and change
+ the data, and then re-import it and re-export it.
+
+ with an attached file, check the result, the alter the data
+ and reimport it.
+-
+ !python {model: edi.document}: |
+ import json
+ partner_obj = self.pool.get('res.partner')
+ tokens = self.export_edi(cr, uid, [partner_obj.browse(cr, uid, ref('base.res_partner_agrolait'))])
+ doc = self.get_document(cr, uid, tokens[0], context=context)
+ edi_doc, = json.loads(doc)
+
+ # check content of the document
+ assert edi_doc.get('__id').endswith('.res_partner_agrolait'), 'Incorrect external ID'
+ assert edi_doc.get('__model') == 'res.partner', 'Incorrect/Missing __model'
+ assert edi_doc.get('__module') == 'base', 'Incorrect/Missing __module'
+ assert edi_doc.get('__last_update'), 'Missing __last_update'
+
+ # try to import the document after changing the name and id, and attaching a
+ # file, and check that a new partner is returned
+ edi_doc['__id'] = 'base:xxd37f8a-xx55-11e0-xxdd-xx81124c8b50.res_partner_xxx'
+ edi_doc['name'] = 'AgroMilk'
+ attachment = {
+ 'name': 'Test file',
+ 'file_name': 'test.png',
+ # base64 standard requires blocks of 57bytes=76chars, NL-separated
+ 'content': 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAKCAYAAACE2W/HAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oIDQ84BkjLWAYAAACNSURBVCjP\njZKxDUJBDEOf/wbUbEHDhkxAzRjUDECBhMQYjIApuPBNpK+PpUinOPYll5Nt1iCJXifbSPpJZtHg\nXLUdu0FWpMEt87a/xtsmqjjNetPFyhuWYFuj7RcgQBNwXNGdw2CqY85yXWi5z7YBnmRyEHvg0YSX\nrLE9P30nwugOHHr+s5va4x+fofAGm1+JjnJICm0AAAAASUVORK5CYII=\n',
+ }
+ edi_doc['__attachments'] = [attachment]
+ doc = json.dumps([edi_doc])
+ result, = self.import_edi(cr, uid, edi_document=doc)
+ assert result[0] == 'res.partner' and result[1] > ref('base.res_partner_agrolait'),\
+ "Expected (%r,> %r) after import 1, got %r" % ('res.partner', ref('base.res_partner_agrolait'), result)
+
+ # export the same partner we just created, and see if the output matches the input
+ tokens = self.export_edi(cr, uid, [partner_obj.browse(cr, uid, result[1])])
+ doc_output = self.get_document(cr, uid, tokens[0], context=context)
+ edi_doc_output, = json.loads(doc_output)
+ for attribute in ('__model', '__module', '__id', 'name', '__attachments'):
+ assert edi_doc_output.get(attribute) == edi_doc.get(attribute), \
+ 'Incorrect value for %s, expected %r, got %r' % (attribute, edi_doc.get(attribute), edi_doc_output.get(attribute))
\ No newline at end of file
diff --git a/addons/email_template/__init__.py b/addons/email_template/__init__.py
index 97931ea8bd5..d1cf5b28431 100644
--- a/addons/email_template/__init__.py
+++ b/addons/email_template/__init__.py
@@ -21,5 +21,6 @@
##############################################################################
import email_template
import wizard
+import res_partner
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/email_template/__openerp__.py b/addons/email_template/__openerp__.py
index 06fa969c0c2..2ab7f194d18 100644
--- a/addons/email_template/__openerp__.py
+++ b/addons/email_template/__openerp__.py
@@ -61,9 +61,13 @@ Openlabs was kept
"data": [
'wizard/email_template_preview_view.xml',
'email_template_view.xml',
+ 'res_partner_view.xml',
'wizard/email_compose_message_view.xml',
'security/ir.model.access.csv'
],
+ "demo": [
+ 'res_partner_demo.yml',
+ ],
"installable": True,
"active": False,
"certificate" : "00817073628967384349",
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index 3917b3f2ddb..237279c16aa 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -28,6 +28,7 @@ from osv import osv
from osv import fields
import tools
from tools.translate import _
+from urllib import quote as quote
try:
from mako.template import Template as MakoTemplate
@@ -66,6 +67,7 @@ class email_template(osv.osv):
user=user,
# context kw would clash with mako internals
ctx=context,
+ quote=quote,
format_exceptions=True)
if result == u'False':
result = u''
@@ -305,6 +307,7 @@ class email_template(osv.osv):
'attachment_ids': False,
'message_id': False,
'state': 'outgoing',
+ 'subtype': 'plain',
}
if not template_id:
return values
@@ -319,6 +322,9 @@ class email_template(osv.osv):
template.model, res_id, context=context) \
or False
+ if values['body_html']:
+ values.update(subtype='html')
+
if template.user_signature:
signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
values['body_text'] += '\n\n' + signature
@@ -355,20 +361,24 @@ class email_template(osv.osv):
values['attachments'] = attachments
return values
- def send_mail(self, cr, uid, template_id, res_id, context=None):
+ def send_mail(self, cr, uid, template_id, res_id, force_send=False, context=None):
"""Generates a new mail message for the given template and record,
- and schedule it for delivery through the ``mail`` module's scheduler.
+ and schedules it for delivery through the ``mail`` module's scheduler.
:param int template_id: id of the template to render
:param int res_id: id of the record to render the template with
(model is taken from the template)
+ :param bool force_send: if True, the generated mail.message is
+ immediately sent after being created, as if the scheduler
+ was executed for this message only.
+ :returns: id of the mail.message that was created
"""
mail_message = self.pool.get('mail.message')
ir_attachment = self.pool.get('ir.attachment')
template = self.browse(cr, uid, template_id, context)
values = self.generate_email(cr, uid, template_id, res_id, context=context)
attachments = values.pop('attachments') or {}
- message_id = mail_message.create(cr, uid, values, context=context)
+ msg_id = mail_message.create(cr, uid, values, context=context)
# link attachments
attachment_ids = []
for fname, fcontent in attachments.iteritems():
@@ -377,10 +387,13 @@ class email_template(osv.osv):
'datas_fname': fname,
'datas': fcontent,
'res_model': mail_message._name,
- 'res_id': message_id,
+ 'res_id': msg_id,
}
if context.has_key('default_type'):
del context['default_type']
- attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context))
+ attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context=context))
+ if force_send:
+ mail_message.send(cr, uid, [msg_id], context=context)
+ return msg_id
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/email_template/email_template_view.xml b/addons/email_template/email_template_view.xml
index d6a651f0bc2..507dae3cd25 100644
--- a/addons/email_template/email_template_view.xml
+++ b/addons/email_template/email_template_view.xml
@@ -155,9 +155,8 @@
-
-
+
diff --git a/addons/email_template/res_partner.py b/addons/email_template/res_partner.py
new file mode 100644
index 00000000000..74d8b4e0693
--- /dev/null
+++ b/addons/email_template/res_partner.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Business Applications
+# Copyright (c) 2011 OpenERP S.A.
+#
+# 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 .
+#
+##############################################################################
+
+from osv import fields,osv
+
+class res_partner(osv.osv):
+ """Inherit res.partner to add a generic opt-out field that can be used
+ to restrict usage of automatic email templates.
+ This field is unused by default. """
+ _inherit = 'res.partner'
+
+ _columns = {
+ 'opt_out': fields.boolean('Opt-out', help="If checked, this partner will not receive any automated email " \
+ "notifications, such as the availability of invoices."),
+ }
+
+ _defaults = {
+ 'opt_out': False,
+ }
\ No newline at end of file
diff --git a/addons/email_template/res_partner_demo.yml b/addons/email_template/res_partner_demo.yml
new file mode 100644
index 00000000000..8eaf48f9e87
--- /dev/null
+++ b/addons/email_template/res_partner_demo.yml
@@ -0,0 +1,9 @@
+-
+ Set opt-out to True on all demo partners
+-
+ !python {model: res.partner}: |
+ partner_ids = self.search(cr, uid, [])
+ # assume partners with an external ID come from demo data
+ ext_ids = self._get_external_ids(cr, uid, partner_ids)
+ ids_to_update = [k for (k,v) in ext_ids.iteritems() if v]
+ self.write(cr, uid, ids_to_update, {'opt_out': True})
\ No newline at end of file
diff --git a/addons/email_template/res_partner_view.xml b/addons/email_template/res_partner_view.xml
new file mode 100644
index 00000000000..0d2f919a96e
--- /dev/null
+++ b/addons/email_template/res_partner_view.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ res.partner.opt_out.form
+ res.partner
+ form
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/email_template/wizard/email_template_preview.py b/addons/email_template/wizard/email_template_preview.py
index 650b714866d..4ef0e9fa156 100644
--- a/addons/email_template/wizard/email_template_preview.py
+++ b/addons/email_template/wizard/email_template_preview.py
@@ -87,6 +87,8 @@ class email_template_preview(osv.osv_memory):
signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
description += '\n' + signature
vals['body_text'] = description
+ if template.body_html:
+ vals['body_html'] = self.render_template(cr, uid, template.body_html, model, res_id, context) or ''
vals['report_name'] = self.render_template(cr, uid, template.report_name, model, res_id, context)
return {'value': vals}
diff --git a/addons/event/board_association_view.xml b/addons/event/board_association_view.xml
index 78fc8395341..47c5e65f196 100644
--- a/addons/event/board_association_view.xml
+++ b/addons/event/board_association_view.xml
@@ -40,24 +40,20 @@
form
diff --git a/addons/event/event.py b/addons/event/event.py
index ba802ef1ffa..ab575408c63 100644
--- a/addons/event/event.py
+++ b/addons/event/event.py
@@ -296,6 +296,7 @@ class event_registration(osv.osv):
return res
_columns = {
+ 'id': fields.integer('ID'),
'name': fields.char('Summary', size=124, readonly=True, states={'draft': [('readonly', False)]}),
'email_cc': fields.text('CC', size=252, readonly=False, states={'done': [('readonly', True)]}, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
'nb_register': fields.integer('Quantity', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Number of Registrations or Tickets"),
diff --git a/addons/event/report/report_event_registration_view.xml b/addons/event/report/report_event_registration_view.xml
index 3fd24c0a366..ef69c674e43 100644
--- a/addons/event/report/report_event_registration_view.xml
+++ b/addons/event/report/report_event_registration_view.xml
@@ -53,10 +53,11 @@
-
-
+
form
tree,graph
- {"search_default_365day":1, "search_default_invoiced":1, "search_default_event":1, 'group_by_no_leaf':1, 'group_by':[]}
+ {"search_default_year":1,"search_default_this_month":1,"search_default_365day":1, "search_default_invoiced":1, "search_default_event":1, 'group_by_no_leaf':1, 'group_by':[]}
diff --git a/addons/fetchmail/i18n/sl.po b/addons/fetchmail/i18n/sl.po
new file mode 100644
index 00000000000..2cdcd56b63d
--- /dev/null
+++ b/addons/fetchmail/i18n/sl.po
@@ -0,0 +1,308 @@
+# Slovenian 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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-11 11:15+0000\n"
+"PO-Revision-Date: 2011-11-22 16:44+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Slovenian \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-23 05:20+0000\n"
+"X-Generator: Launchpad (build 14336)\n"
+
+#. module: fetchmail
+#: constraint:email.server:0
+msgid ""
+"Warning! Record for selected Model can not be created\n"
+"Please choose valid Model"
+msgstr ""
+
+#. module: fetchmail
+#: selection:email.server,state:0
+msgid "Confirmed"
+msgstr "Potrjeno"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Confirm"
+msgstr "Potrdi"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Group By..."
+msgstr "Združi po ..."
+
+#. module: fetchmail
+#: view:email.server:0
+#: field:email.server,state:0
+msgid "State"
+msgstr "Država"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "POP"
+msgstr "POP"
+
+#. module: fetchmail
+#: selection:email.server,state:0
+msgid "Not Confirmed"
+msgstr "Nepotrjeno"
+
+#. module: fetchmail
+#: field:email.server,user:0
+msgid "User Name"
+msgstr "Uporabniško Ime"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Type"
+msgstr "Tip"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "POP/IMAP Servers"
+msgstr "POP/IMAP Strežniki"
+
+#. module: fetchmail
+#: model:ir.module.module,shortdesc:fetchmail.module_meta_information
+msgid "Fetchmail Server"
+msgstr ""
+
+#. module: fetchmail
+#: view:email.server:0
+#: field:email.server,note:0
+msgid "Description"
+msgstr "Opis"
+
+#. module: fetchmail
+#: help:email.server,object_id:0
+msgid ""
+"OpenObject Model. Generates a record of this model.\n"
+"Select Object with message_new attrbutes."
+msgstr ""
+
+#. module: fetchmail
+#: field:email.server,attach:0
+msgid "Add Attachments ?"
+msgstr "Dodaj Priloge ?"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "# of emails"
+msgstr "# od emalov"
+
+#. module: fetchmail
+#: model:ir.actions.act_window,name:fetchmail.act_server_history
+msgid "Email History"
+msgstr ""
+
+#. module: fetchmail
+#: field:email.server,user_id:0
+msgid "User"
+msgstr "Uporabnik"
+
+#. module: fetchmail
+#: field:email.server,date:0
+msgid "Date"
+msgstr "Datum"
+
+#. module: fetchmail
+#: selection:email.server,state:0
+msgid "Waiting for Verification"
+msgstr "Čaka na Potrditev"
+
+#. module: fetchmail
+#: field:email.server,password:0
+msgid "Password"
+msgstr "Geslo"
+
+#. module: fetchmail
+#: model:ir.actions.act_window,name:fetchmail.act_server_history
+#: view:mailgate.message:0
+msgid "Emails"
+msgstr "E-pošte"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Search Email Servers"
+msgstr "Poišči Email Strežnik"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Server & Login"
+msgstr "Strežnik in Prijava"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Auto Reply?"
+msgstr "Avtomatski Odzivnik?"
+
+#. module: fetchmail
+#: field:email.server,name:0
+msgid "Name"
+msgstr "Ime"
+
+#. module: fetchmail
+#: model:ir.model,name:fetchmail.model_mailgate_message
+msgid "Mailgateway Message"
+msgstr ""
+
+#. module: fetchmail
+#: model:ir.actions.act_window,name:fetchmail.action_email_server_tree
+msgid "POP Servers"
+msgstr ""
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Set to Draft"
+msgstr ""
+
+#. module: fetchmail
+#: field:email.server,message_ids:0
+#: model:ir.actions.act_window,name:fetchmail.action_view_mail_message_emails
+msgid "Messages"
+msgstr "Sporočila"
+
+#. module: fetchmail
+#: model:ir.ui.menu,name:fetchmail.menu_action_fetchmail_server_tree
+msgid "Fetchmail Services"
+msgstr ""
+
+#. module: fetchmail
+#: field:email.server,server:0
+msgid "Server"
+msgstr "Strežnik"
+
+#. module: fetchmail
+#: field:email.server,active:0
+msgid "Active"
+msgstr "Aktiven"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Process Parameter"
+msgstr "Parametri Procesa"
+
+#. module: fetchmail
+#: field:email.server,is_ssl:0
+msgid "SSL ?"
+msgstr "SSL ?"
+
+#. module: fetchmail
+#: selection:email.server,type:0
+#: selection:mailgate.message,server_type:0
+msgid "IMAP Server"
+msgstr "IMAP Strežnik"
+
+#. module: fetchmail
+#: field:email.server,object_id:0
+msgid "Model"
+msgstr ""
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "IMAP"
+msgstr "IMAP"
+
+#. module: fetchmail
+#: view:email.server:0
+#: model:ir.model,name:fetchmail.model_email_server
+msgid "POP/IMAP Server"
+msgstr "POP/IMAP Strežnik"
+
+#. module: fetchmail
+#: constraint:email.server:0
+msgid "Warning! Can't have duplicate server configuration!"
+msgstr "Opozorilo! Ne morete podvojiti konfiguracije strežnika!"
+
+#. module: fetchmail
+#: field:email.server,type:0
+#: field:mailgate.message,server_type:0
+msgid "Server Type"
+msgstr "Tip Strežnika"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Login Information"
+msgstr "Prijavni podatki"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Server Information"
+msgstr "Podatki o strežniku"
+
+#. module: fetchmail
+#: help:email.server,attach:0
+msgid "Fetches mail with attachments if true."
+msgstr ""
+
+#. module: fetchmail
+#: selection:email.server,type:0
+#: selection:mailgate.message,server_type:0
+msgid "POP Server"
+msgstr "POP Strežnik"
+
+#. module: fetchmail
+#: field:email.server,port:0
+msgid "Port"
+msgstr "Vrata"
+
+#. module: fetchmail
+#: model:ir.module.module,description:fetchmail.module_meta_information
+msgid ""
+"Fetchmail: \n"
+" * Fetch email from Pop / IMAP server\n"
+" * Support SSL\n"
+" * Integrated with all Modules\n"
+" * Automatic Email Receive\n"
+" * Email based Records (Add, Update)\n"
+" "
+msgstr ""
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "SSL"
+msgstr "SSL"
+
+#. module: fetchmail
+#: help:email.server,action_id:0
+msgid ""
+"An Email Server Action. It will be run whenever an e-mail is fetched from "
+"server."
+msgstr ""
+
+#. module: fetchmail
+#: help:email.server,priority:0
+msgid "Priority between 0 to 10, select define the order of Processing"
+msgstr ""
+
+#. module: fetchmail
+#: field:email.server,action_id:0
+msgid "Email Server Action"
+msgstr ""
+
+#. module: fetchmail
+#: field:email.server,priority:0
+msgid "Server Priority"
+msgstr ""
+
+#. module: fetchmail
+#: view:mailgate.message:0
+#: field:mailgate.message,server_id:0
+msgid "Mail Server"
+msgstr "Poštni Strežnik"
+
+#. module: fetchmail
+#: view:email.server:0
+msgid "Fetch Emails"
+msgstr ""
+
+#~ msgid "Email Servers"
+#~ msgstr "Email Strežniki"
diff --git a/addons/hr/hr_board.xml b/addons/hr/hr_board.xml
index 874b0d368d6..620f1f51c88 100644
--- a/addons/hr/hr_board.xml
+++ b/addons/hr/hr_board.xml
@@ -8,12 +8,12 @@
form
@@ -27,7 +27,7 @@
-
+
@@ -38,12 +38,12 @@
form
diff --git a/addons/hr/hr_demo.xml b/addons/hr/hr_demo.xml
index dbfead793e5..15269a88375 100644
--- a/addons/hr/hr_demo.xml
+++ b/addons/hr/hr_demo.xml
@@ -54,17 +54,40 @@
-
- Quentin Paolino
-
-
-
-
-
Antoine Philippe
+
+
+
+
+
+ Quentin Paolino
+
+
+
+
+
+
+
+ John Smith
+
+
+
+
+
+
+ Thomas McQueen
+
+
+
+
+
+
+ Vanessa Dupont
+
+
diff --git a/addons/hr/hr_department_demo.xml b/addons/hr/hr_department_demo.xml
index 433e77a6dd4..5ac57db52a3 100644
--- a/addons/hr/hr_department_demo.xml
+++ b/addons/hr/hr_department_demo.xml
@@ -26,31 +26,34 @@
IT
+
Marketing
-
Sales
+
Sales Europe
+
Sales America
+
@@ -106,7 +109,19 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml
index 8b16022ec91..bbe2d00ff6f 100644
--- a/addons/hr/hr_view.xml
+++ b/addons/hr/hr_view.xml
@@ -344,6 +344,7 @@
hr.employee
form
tree,form
+ [('category_ids','=', active_ids)]
@@ -351,7 +352,6 @@
Employees by Categories
-
, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-11 11:15+0000\n"
+"PO-Revision-Date: 2011-11-21 12:43+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Bengali \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-22 05:00+0000\n"
+"X-Generator: Launchpad (build 14299)\n"
+
+#. module: hr
+#: model:process.node,name:hr.process_node_openerpuser0
+msgid "Openerp user"
+msgstr "Openerp ব্যাবহারকারী"
+
+#. module: hr
+#: view:hr.job:0
+#: field:hr.job,requirements:0
+msgid "Requirements"
+msgstr "আবশ্যকতা"
+
+#. module: hr
+#: constraint:hr.department:0
+msgid "Error! You can not create recursive departments."
+msgstr "ভুল! আপনি রিকারসিভ দপ্তর তৈরি করতে পারবেন না।"
+
+#. module: hr
+#: model:process.transition,name:hr.process_transition_contactofemployee0
+msgid "Link the employee to information"
+msgstr "তথ্যের সাথে কর্মচারী যুক্ত করুন"
+
+#. module: hr
+#: field:hr.employee,sinid:0
+msgid "SIN No"
+msgstr "ন্যাশনাল আইডি নং"
+
+#. module: hr
+#: model:ir.module.module,shortdesc:hr.module_meta_information
+#: model:ir.ui.menu,name:hr.menu_hr_deshboard
+#: model:ir.ui.menu,name:hr.menu_hr_main
+#: model:ir.ui.menu,name:hr.menu_hr_management
+#: model:ir.ui.menu,name:hr.menu_hr_root
+msgid "Human Resources"
+msgstr "জনসম্পদ"
+
+#. module: hr
+#: view:hr.employee:0
+#: view:hr.job:0
+msgid "Group By..."
+msgstr "...দিয়ে গ্রুপ করুন"
+
+#. module: hr
+#: model:ir.actions.act_window,help:hr.action_hr_job
+msgid ""
+"Job Positions are used to define jobs and their requirements. You can keep "
+"track of the number of employees you have per job position and how many you "
+"expect in the future. You can also attach a survey to a job position that "
+"will be used in the recruitment process to evaluate the applicants for this "
+"job position."
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+#: field:hr.employee,department_id:0
+#: view:hr.job:0
+#: field:hr.job,department_id:0
+#: view:res.users:0
+msgid "Department"
+msgstr "বিভাগ"
+
+#. module: hr
+#: help:hr.installer,hr_attendance:0
+msgid "Simplifies the management of employee's attendances."
+msgstr "কর্মচারী উপস্থিতি ম্যানেজমেন্ট সহজতর করে"
+
+#. module: hr
+#: view:hr.job:0
+msgid "Mark as Old"
+msgstr ""
+
+#. module: hr
+#: view:hr.job:0
+msgid "Jobs"
+msgstr "কাজ"
+
+#. module: hr
+#: view:hr.job:0
+msgid "In Recruitment"
+msgstr "নিয়োগপ্রাপ্ত"
+
+#. module: hr
+#: view:hr.installer:0
+msgid "title"
+msgstr "শিরোনাম"
+
+#. module: hr
+#: field:hr.department,company_id:0
+#: view:hr.employee:0
+#: view:hr.job:0
+#: field:hr.job,company_id:0
+msgid "Company"
+msgstr "কোম্পানি"
+
+#. module: hr
+#: field:hr.job,no_of_recruitment:0
+msgid "Expected in Recruitment"
+msgstr "নিয়োগে কাম্য"
+
+#. module: hr
+#: model:ir.ui.menu,name:hr.menu_open_view_attendance_reason_config
+msgid "Holidays"
+msgstr "ছুটির দিন"
+
+#. module: hr
+#: help:hr.installer,hr_holidays:0
+msgid "Tracks employee leaves, allocation requests and planning."
+msgstr "কর্মচারীদের ছুটির হিসাব রাখে"
+
+#. module: hr
+#: model:ir.model,name:hr.model_hr_employee_marital_status
+msgid "Employee Marital Status"
+msgstr "কর্মচারীর বৈবাহিক অবস্থা"
+
+#. module: hr
+#: help:hr.employee,partner_id:0
+msgid ""
+"Partner that is related to the current employee. Accounting transaction will "
+"be written on this partner belongs to employee."
+msgstr ""
+
+#. module: hr
+#: model:process.transition,name:hr.process_transition_employeeuser0
+msgid "Link a user to an employee"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_contract:0
+msgid "Employee's Contracts"
+msgstr ""
+
+#. module: hr
+#: help:hr.installer,hr_payroll:0
+msgid "Generic Payroll system."
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+msgid "My Departments Employee"
+msgstr ""
+
+#. module: hr
+#: model:hr.employee.marital.status,name:hr.hr_employee_marital_status_married
+msgid "Married"
+msgstr ""
+
+#. module: hr
+#: constraint:hr.employee:0
+msgid ""
+"Error ! You cannot select a department for which the employee is the manager."
+msgstr ""
+
+#. module: hr
+#: help:hr.employee,passport_id:0
+msgid "Employee Passport Information"
+msgstr ""
+
+#. module: hr
+#: model:ir.actions.act_window,help:hr.open_module_tree_department
+msgid ""
+"Your Company's Department Structure is used to manage all documents related "
+"to employees by departments: expenses and timesheet validation, leaves "
+"management, recruitments, etc."
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+msgid "Position"
+msgstr "পদ"
+
+#. module: hr
+#: model:ir.actions.act_window,name:hr.action2
+msgid "Employee Hierarchy"
+msgstr ""
+
+#. module: hr
+#: model:process.transition,note:hr.process_transition_employeeuser0
+msgid ""
+"The Related user field on the Employee form allows to link the OpenERP user "
+"(and her rights) to the employee."
+msgstr ""
+
+#. module: hr
+#: view:hr.job:0
+#: selection:hr.job,state:0
+msgid "In Recruitement"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,identification_id:0
+msgid "Identification No"
+msgstr ""
+
+#. module: hr
+#: field:hr.job,no_of_employee:0
+msgid "No of Employee"
+msgstr ""
+
+#. module: hr
+#: selection:hr.employee,gender:0
+msgid "Female"
+msgstr "মহিলা"
+
+#. module: hr
+#: help:hr.installer,hr_timesheet_sheet:0
+msgid ""
+"Tracks and helps employees encode and validate timesheets and attendances."
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_evaluation:0
+msgid "Periodic Evaluations"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_timesheet_sheet:0
+msgid "Timesheets"
+msgstr ""
+
+#. module: hr
+#: model:ir.actions.act_window,name:hr.open_view_employee_tree
+msgid "Employees Structure"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+msgid "Social IDs"
+msgstr ""
+
+#. module: hr
+#: help:hr.job,no_of_employee:0
+msgid "Number of employee with that job."
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,work_phone:0
+msgid "Work Phone"
+msgstr "কর্মস্থলের ফোন"
+
+#. module: hr
+#: field:hr.employee.category,child_ids:0
+msgid "Child Categories"
+msgstr ""
+
+#. module: hr
+#: view:hr.job:0
+#: field:hr.job,description:0
+#: model:ir.model,name:hr.model_hr_job
+msgid "Job Description"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,work_location:0
+msgid "Office Location"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+#: view:hr.job:0
+#: model:ir.model,name:hr.model_hr_employee
+#: model:process.node,name:hr.process_node_employee0
+msgid "Employee"
+msgstr ""
+
+#. module: hr
+#: model:process.node,note:hr.process_node_employeecontact0
+msgid "Other information"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,work_email:0
+msgid "Work E-mail"
+msgstr ""
+
+#. module: hr
+#: field:hr.department,complete_name:0
+#: field:hr.employee.category,complete_name:0
+msgid "Name"
+msgstr "নাম"
+
+#. module: hr
+#: field:hr.employee,birthday:0
+msgid "Date of Birth"
+msgstr "জন্মদিন"
+
+#. module: hr
+#: model:ir.ui.menu,name:hr.menu_hr_reporting
+msgid "Reporting"
+msgstr ""
+
+#. module: hr
+#: model:ir.model,name:hr.model_ir_actions_act_window
+msgid "ir.actions.act_window"
+msgstr ""
+
+#. module: hr
+#: model:ir.actions.act_window,name:hr.open_board_hr
+msgid "Human Resources Dashboard"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+#: field:hr.employee,job_id:0
+#: view:hr.job:0
+msgid "Job"
+msgstr ""
+
+#. module: hr
+#: view:hr.department:0
+#: field:hr.department,member_ids:0
+msgid "Members"
+msgstr ""
+
+#. module: hr
+#: model:ir.ui.menu,name:hr.menu_hr_configuration
+msgid "Configuration"
+msgstr ""
+
+#. module: hr
+#: view:hr.installer:0
+msgid ""
+"You can enhance the base HR Application by installing few HR-related "
+"functionalities."
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+msgid "Categories"
+msgstr ""
+
+#. module: hr
+#: field:hr.job,expected_employees:0
+msgid "Expected Employees"
+msgstr ""
+
+#. module: hr
+#: help:hr.employee,sinid:0
+msgid "Social Insurance Number"
+msgstr ""
+
+#. module: hr
+#: model:hr.employee.marital.status,name:hr.hr_employee_marital_status_divorced
+msgid "Divorced"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee.category,parent_id:0
+msgid "Parent Category"
+msgstr ""
+
+#. module: hr
+#: constraint:hr.employee.category:0
+msgid "Error ! You cannot create recursive Categories."
+msgstr ""
+
+#. module: hr
+#: view:hr.department:0
+#: model:ir.actions.act_window,name:hr.open_module_tree_department
+#: model:ir.ui.menu,name:hr.menu_hr_department_tree
+#: view:res.users:0
+#: field:res.users,context_department_id:0
+msgid "Departments"
+msgstr ""
+
+#. module: hr
+#: model:process.node,name:hr.process_node_employeecontact0
+msgid "Employee Contact"
+msgstr ""
+
+#. module: hr
+#: view:board.board:0
+msgid "My Board"
+msgstr ""
+
+#. module: hr
+#: selection:hr.employee,gender:0
+msgid "Male"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,progress:0
+msgid "Configuration Progress"
+msgstr ""
+
+#. module: hr
+#: model:ir.actions.act_window,name:hr.open_view_categ_form
+#: model:ir.ui.menu,name:hr.menu_view_employee_category_form
+msgid "Categories of Employee"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee.category:0
+#: model:ir.model,name:hr.model_hr_employee_category
+msgid "Employee Category"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,config_logo:0
+msgid "Image"
+msgstr ""
+
+#. module: hr
+#: model:process.process,name:hr.process_process_employeecontractprocess0
+msgid "Employee Contract"
+msgstr ""
+
+#. module: hr
+#: help:hr.installer,hr_evaluation:0
+msgid ""
+"Lets you create and manage the periodic evaluation and performance review of "
+"employees."
+msgstr ""
+
+#. module: hr
+#: model:ir.model,name:hr.model_hr_department
+msgid "hr.department"
+msgstr ""
+
+#. module: hr
+#: help:hr.employee,parent_id:0
+msgid "It is linked with manager of Department"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_recruitment:0
+msgid "Recruitment Process"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,category_ids:0
+#: field:hr.employee.category,name:0
+msgid "Category"
+msgstr ""
+
+#. module: hr
+#: model:ir.actions.act_window,help:hr.open_view_employee_list_my
+msgid ""
+"Here you can manage your work force by creating employees and assigning them "
+"specific properties in the system. Maintain all employee related information "
+"and keep track of anything that needs to be recorded for them. The personal "
+"information tab will help you maintain their identity data. The Categories "
+"tab gives you the opportunity to assign them related employee categories "
+"depending on their position and activities within the company. A category "
+"can be a seniority level within the company or a department. The Timesheets "
+"tab allows to assign them a specific timesheet and analytic journal where "
+"they will be able to enter time through the system. In the note tab, you can "
+"enter text data that should be recorded for a specific employee."
+msgstr ""
+
+#. module: hr
+#: help:hr.employee,bank_account_id:0
+msgid "Employee bank salary account"
+msgstr ""
+
+#. module: hr
+#: field:hr.department,note:0
+msgid "Note"
+msgstr ""
+
+#. module: hr
+#: constraint:res.users:0
+msgid "The chosen company is not in the allowed companies for this user"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+msgid "Contact Information"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,address_id:0
+msgid "Working Address"
+msgstr ""
+
+#. module: hr
+#: model:ir.actions.act_window,name:hr.open_board_hr_manager
+msgid "HR Manager Dashboard"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+msgid "Status"
+msgstr ""
+
+#. module: hr
+#: view:hr.installer:0
+msgid "Configure"
+msgstr ""
+
+#. module: hr
+#: model:ir.actions.act_window,name:hr.open_view_categ_tree
+#: model:ir.ui.menu,name:hr.menu_view_employee_category_tree
+msgid "Categories structure"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,partner_id:0
+msgid "unknown"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_holidays:0
+msgid "Holidays / Leaves Management"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,ssnid:0
+msgid "SSN No"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+msgid "Active"
+msgstr ""
+
+#. module: hr
+#: constraint:hr.employee:0
+msgid "Error ! You cannot create recursive Hierarchy of Employees."
+msgstr ""
+
+#. module: hr
+#: view:hr.department:0
+msgid "Companies"
+msgstr ""
+
+#. module: hr
+#: model:ir.module.module,description:hr.module_meta_information
+msgid ""
+"\n"
+" Module for human resource management. You can manage:\n"
+" * Employees and hierarchies : You can define your employee with User and "
+"display hierarchies\n"
+" * HR Departments\n"
+" * HR Jobs\n"
+" "
+msgstr ""
+
+#. module: hr
+#: model:process.transition,note:hr.process_transition_contactofemployee0
+msgid ""
+"In the Employee form, there are different kind of information like Contact "
+"information."
+msgstr ""
+
+#. module: hr
+#: help:hr.job,expected_employees:0
+msgid "Required number of Employees in total for that job."
+msgstr ""
+
+#. module: hr
+#: selection:hr.job,state:0
+msgid "Old"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee.marital.status,description:0
+msgid "Status Description"
+msgstr ""
+
+#. module: hr
+#: sql_constraint:res.users:0
+msgid "You can not have two users with the same login !"
+msgstr ""
+
+#. module: hr
+#: view:hr.job:0
+#: field:hr.job,state:0
+msgid "State"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,marital:0
+#: view:hr.employee.marital.status:0
+#: field:hr.employee.marital.status,name:0
+#: model:ir.actions.act_window,name:hr.action_hr_marital_status
+#: model:ir.ui.menu,name:hr.hr_menu_marital_status
+msgid "Marital Status"
+msgstr ""
+
+#. module: hr
+#: help:hr.installer,hr_recruitment:0
+msgid "Helps you manage and streamline your recruitment process."
+msgstr ""
+
+#. module: hr
+#: model:process.node,note:hr.process_node_employee0
+msgid "Employee form and structure"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,photo:0
+msgid "Photo"
+msgstr ""
+
+#. module: hr
+#: model:ir.model,name:hr.model_res_users
+msgid "res.users"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_payroll_account:0
+msgid "Payroll Accounting"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+msgid "Personal Information"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,passport_id:0
+msgid "Passport No"
+msgstr ""
+
+#. module: hr
+#: view:res.users:0
+msgid "Current Activity"
+msgstr ""
+
+#. module: hr
+#: help:hr.installer,hr_expense:0
+msgid ""
+"Tracks and manages employee expenses, and can automatically re-invoice "
+"clients if the expenses are project-related."
+msgstr ""
+
+#. module: hr
+#: view:hr.job:0
+msgid "Current"
+msgstr ""
+
+#. module: hr
+#: field:hr.department,parent_id:0
+msgid "Parent Department"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee.category:0
+msgid "Employees Categories"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,address_home_id:0
+msgid "Home Address"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_attendance:0
+#: model:ir.ui.menu,name:hr.menu_open_view_attendance_reason_new_config
+msgid "Attendances"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee.marital.status:0
+#: view:hr.job:0
+msgid "Description"
+msgstr ""
+
+#. module: hr
+#: help:hr.installer,hr_contract:0
+msgid "Extends employee profiles to help manage their contracts."
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_payroll:0
+msgid "Payroll"
+msgstr ""
+
+#. module: hr
+#: model:hr.employee.marital.status,name:hr.hr_employee_marital_status_single
+msgid "Single"
+msgstr ""
+
+#. module: hr
+#: field:hr.job,name:0
+msgid "Job Name"
+msgstr ""
+
+#. module: hr
+#: view:hr.job:0
+#: selection:hr.job,state:0
+msgid "In Position"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,mobile_phone:0
+msgid "Mobile"
+msgstr ""
+
+#. module: hr
+#: view:hr.department:0
+msgid "department"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,country_id:0
+msgid "Nationality"
+msgstr ""
+
+#. module: hr
+#: view:hr.department:0
+#: view:hr.employee:0
+#: field:hr.employee,notes:0
+msgid "Notes"
+msgstr ""
+
+#. module: hr
+#: model:ir.model,name:hr.model_hr_installer
+msgid "hr.installer"
+msgstr ""
+
+#. module: hr
+#: view:board.board:0
+msgid "HR Manager Board"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,resource_id:0
+msgid "Resource"
+msgstr ""
+
+#. module: hr
+#: view:hr.installer:0
+#: model:ir.actions.act_window,name:hr.action_hr_installer
+msgid "Human Resources Application Configuration"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,gender:0
+msgid "Gender"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+#: field:hr.job,employee_ids:0
+#: model:ir.actions.act_window,name:hr.hr_employee_normal_action_tree
+#: model:ir.actions.act_window,name:hr.open_view_employee_list
+#: model:ir.actions.act_window,name:hr.open_view_employee_list_my
+#: model:ir.ui.menu,name:hr.menu_open_view_employee_list_my
+#: model:ir.ui.menu,name:hr.menu_view_employee_category_configuration_form
+msgid "Employees"
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,bank_account_id:0
+msgid "Bank Account"
+msgstr ""
+
+#. module: hr
+#: field:hr.department,name:0
+msgid "Department Name"
+msgstr ""
+
+#. module: hr
+#: help:hr.employee,ssnid:0
+msgid "Social Security Number"
+msgstr ""
+
+#. module: hr
+#: model:process.node,note:hr.process_node_openerpuser0
+msgid "Creation of a OpenERP user"
+msgstr ""
+
+#. module: hr
+#: field:hr.department,child_ids:0
+msgid "Child Departments"
+msgstr ""
+
+#. module: hr
+#: model:ir.actions.act_window,name:hr.action_hr_job
+#: model:ir.ui.menu,name:hr.menu_hr_job
+msgid "Job Positions"
+msgstr ""
+
+#. module: hr
+#: view:hr.employee:0
+#: field:hr.employee,coach_id:0
+msgid "Coach"
+msgstr ""
+
+#. module: hr
+#: view:hr.installer:0
+msgid "Configure Your Human Resources Application"
+msgstr ""
+
+#. module: hr
+#: field:hr.installer,hr_expense:0
+msgid "Expenses"
+msgstr ""
+
+#. module: hr
+#: field:hr.department,manager_id:0
+#: view:hr.employee:0
+#: field:hr.employee,parent_id:0
+msgid "Manager"
+msgstr ""
+
+#. module: hr
+#: model:hr.employee.marital.status,name:hr.hr_employee_marital_status_widower
+msgid "Widower"
+msgstr ""
+
+#. module: hr
+#: help:hr.installer,hr_payroll_account:0
+msgid "Generic Payroll system Integrated with Accountings."
+msgstr ""
+
+#. module: hr
+#: field:hr.employee,child_ids:0
+msgid "Subordinates"
+msgstr ""
diff --git a/addons/hr/i18n/fr_CH.po b/addons/hr/i18n/fr_CH.po
new file mode 100644
index 00000000000..650fcdb8b92
--- /dev/null
+++ b/addons/hr/i18n/fr_CH.po
@@ -0,0 +1,29 @@
+# French (Switzerland) 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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-11 11:15:13+0000\n"
+"PO-Revision-Date: 2011-11-06 15:15+0000\n"
+"Last-Translator: Bogdan Stanciu (bstanciu)\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-05 05:31+0000\n"
+"X-Generator: Launchpad (build 14231)\n"
+
+#. module: hr
+#: field:hr.employee,ssnid:0
+msgid "SSN No"
+msgstr "No. AVS"
+
+#. module: hr
+#: help:hr.employee,ssnid:0
+msgid "Social Security Number"
+msgstr "Numéro de sécurité sociale (AVS)"
+
diff --git a/addons/hr/test/open2recruit2close_job.yml b/addons/hr/test/open2recruit2close_job.yml
index 597db098a29..a33aaa7e60f 100644
--- a/addons/hr/test/open2recruit2close_job.yml
+++ b/addons/hr/test/open2recruit2close_job.yml
@@ -9,7 +9,7 @@
I check state of Job Position after opened it.
-
!assert {model: hr.job, id: job_jr_appli}:
- - state == 'open'
+ - state == 'open
-
Now, Recruitement is started so I start recruitement of Job Postion of "Jr. Application Engineer" Profile.
-
diff --git a/addons/hr_attendance/wizard/hr_attendance_bymonth_view.xml b/addons/hr_attendance/wizard/hr_attendance_bymonth_view.xml
index e9b5fecf330..660cdbc81cd 100644
--- a/addons/hr_attendance/wizard/hr_attendance_bymonth_view.xml
+++ b/addons/hr_attendance/wizard/hr_attendance_bymonth_view.xml
@@ -29,7 +29,6 @@
-
Attendances By Month
client_print_multi
diff --git a/addons/hr_attendance/wizard/hr_attendance_byweek_view.xml b/addons/hr_attendance/wizard/hr_attendance_byweek_view.xml
index 4ed56ce1800..63cd0978a7f 100644
--- a/addons/hr_attendance/wizard/hr_attendance_byweek_view.xml
+++ b/addons/hr_attendance/wizard/hr_attendance_byweek_view.xml
@@ -28,7 +28,6 @@
-
Attendances By Week
client_print_multi
diff --git a/addons/hr_attendance/wizard/hr_attendance_error_view.xml b/addons/hr_attendance/wizard/hr_attendance_error_view.xml
index 6eec4701fb4..8f943d7d1da 100644
--- a/addons/hr_attendance/wizard/hr_attendance_error_view.xml
+++ b/addons/hr_attendance/wizard/hr_attendance_error_view.xml
@@ -32,7 +32,6 @@
-
Attendance Error Report
client_print_multi
diff --git a/addons/hr_evaluation/__openerp__.py b/addons/hr_evaluation/__openerp__.py
index f082d9bfcef..720c49302d4 100644
--- a/addons/hr_evaluation/__openerp__.py
+++ b/addons/hr_evaluation/__openerp__.py
@@ -39,7 +39,7 @@ level of employee hierarchy fills what and final review and evaluation
is done by the manager.Every evaluation filled by the employees can be viewed
in the form of pdf file. Implements a dashboard for My Current Evaluations
""",
- "demo": [],
+ "demo": ["hr_evaluation_demo.xml"],
"data": [
"security/ir.model.access.csv",
"security/hr_evaluation_security.xml",
diff --git a/addons/hr_evaluation/board_hr_evaluation_view.xml b/addons/hr_evaluation/board_hr_evaluation_view.xml
index 70d6245b1ee..7f6d3142db5 100644
--- a/addons/hr_evaluation/board_hr_evaluation_view.xml
+++ b/addons/hr_evaluation/board_hr_evaluation_view.xml
@@ -10,15 +10,15 @@
[('state','in',('wait', 'progress')),('create_uid','=',uid)]
-
+
board.hr.evaluation.form
board.board
-
+
form
-
-
+
+
diff --git a/addons/hr_evaluation/hr_evaluation_data.xml b/addons/hr_evaluation/hr_evaluation_data.xml
index 6745b997b3f..40ff397534e 100644
--- a/addons/hr_evaluation/hr_evaluation_data.xml
+++ b/addons/hr_evaluation/hr_evaluation_data.xml
@@ -1210,23 +1210,11 @@ Once the form had been filled, the employee send it to his supervisor.
-
+
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/hr_evaluation/hr_evaluation_demo.xml b/addons/hr_evaluation/hr_evaluation_demo.xml
new file mode 100644
index 00000000000..db096f13266
--- /dev/null
+++ b/addons/hr_evaluation/hr_evaluation_demo.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/hr_evaluation/report/hr_evaluation_report_view.xml b/addons/hr_evaluation/report/hr_evaluation_report_view.xml
index fc001937f62..c70c1d27923 100644
--- a/addons/hr_evaluation/report/hr_evaluation_report_view.xml
+++ b/addons/hr_evaluation/report/hr_evaluation_report_view.xml
@@ -47,8 +47,10 @@
-
+
@@ -95,7 +97,7 @@
hr.evaluation.report
form
tree,graph
- {'search_default_month':1,'search_default_employee':1,'group_by_no_leaf':1,'group_by':[]}
+ {'search_default_year':1,'search_default_month':1,'search_default_employee':1,'group_by_no_leaf':1,'group_by':[]}
diff --git a/addons/hr_expense/board_hr_expense_manager_view.xml b/addons/hr_expense/board_hr_expense_manager_view.xml
index c45979057a1..0f09c9a3290 100644
--- a/addons/hr_expense/board_hr_expense_manager_view.xml
+++ b/addons/hr_expense/board_hr_expense_manager_view.xml
@@ -16,8 +16,8 @@
form
-
-
+
+
diff --git a/addons/hr_expense/board_hr_expense_view.xml b/addons/hr_expense/board_hr_expense_view.xml
index 4e213048dc7..5d6ad4dbd6b 100644
--- a/addons/hr_expense/board_hr_expense_view.xml
+++ b/addons/hr_expense/board_hr_expense_view.xml
@@ -16,8 +16,8 @@
form
-
-
+
+
diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py
index 201c70c642c..478b8120935 100644
--- a/addons/hr_expense/hr_expense.py
+++ b/addons/hr_expense/hr_expense.py
@@ -137,8 +137,9 @@ class hr_expense_expense(osv.osv):
inv_ids.append(self.browse(cr, uid, id).invoice_id.id)
return {
'name': _('Supplier Invoices'),
+ 'view_type': 'form',
'view_mode': 'form',
- 'view_id': [res[1] if res else False],
+ 'view_id': [res and res[1] or False],
'res_model': 'account.invoice',
'context': "{'type':'out_invoice', 'journal_type': 'purchase'}",
'type': 'ir.actions.act_window',
diff --git a/addons/hr_expense/hr_expense_workflow.xml b/addons/hr_expense/hr_expense_workflow.xml
index 7946bef3b6b..328a07879a4 100644
--- a/addons/hr_expense/hr_expense_workflow.xml
+++ b/addons/hr_expense/hr_expense_workflow.xml
@@ -104,13 +104,6 @@
-
-
- subflow.cancel
-
-
-
-
draft
diff --git a/addons/hr_expense/report/hr_expense_report_view.xml b/addons/hr_expense/report/hr_expense_report_view.xml
index 680003b88be..0685880b863 100644
--- a/addons/hr_expense/report/hr_expense_report_view.xml
+++ b/addons/hr_expense/report/hr_expense_report_view.xml
@@ -53,10 +53,11 @@
-
-
+
@@ -113,7 +114,7 @@
hr.expense.report
form
tree,graph
- {'search_default_month':1,'search_default_employee':1,'group_by_no_leaf':1,'group_by':[]}
+ {'search_default_year':1,'search_default_month':1,'search_default_employee':1,'group_by_no_leaf':1,'group_by':[]}
diff --git a/addons/hr_holidays/board_hr_holidays_view.xml b/addons/hr_holidays/board_hr_holidays_view.xml
index 9dc9646da9b..f0a0ab0741a 100644
--- a/addons/hr_holidays/board_hr_holidays_view.xml
+++ b/addons/hr_holidays/board_hr_holidays_view.xml
@@ -16,8 +16,8 @@
form
-
-
+
+
diff --git a/addons/hr_holidays/board_hr_manager_holidays_view.xml b/addons/hr_holidays/board_hr_manager_holidays_view.xml
index 662569fb81d..1c7c6280f8b 100644
--- a/addons/hr_holidays/board_hr_manager_holidays_view.xml
+++ b/addons/hr_holidays/board_hr_manager_holidays_view.xml
@@ -18,8 +18,8 @@
form
-
-
+
+
diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py
index f6f660f5f45..d22c5b9f1bb 100644
--- a/addons/hr_holidays/hr_holidays.py
+++ b/addons/hr_holidays/hr_holidays.py
@@ -151,16 +151,26 @@ class hr_holidays(osv.osv):
('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0 !"),
]
- def _create_resource_leave(self, cr, uid, vals, context=None):
+ def _create_resource_leave(self, cr, uid, leaves, context=None):
'''This method will create entry in resource calendar leave object at the time of holidays validated '''
obj_res_leave = self.pool.get('resource.calendar.leaves')
- return obj_res_leave.create(cr, uid, vals, context=context)
+ for leave in leaves:
+ vals = {
+ 'name': leave.name,
+ 'date_from': leave.date_from,
+ 'holiday_id': leave.id,
+ 'date_to': leave.date_to,
+ 'resource_id': leave.employee_id.resource_id.id,
+ 'calendar_id': leave.employee_id.resource_id.calendar_id.id
+ }
+ obj_res_leave.create(cr, uid, vals, context=context)
+ return True
- def _remove_resouce_leave(self, cr, uid, ids, context=None):
+ def _remove_resource_leave(self, cr, uid, ids, context=None):
'''This method will create entry in resource calendar leave object at the time of holidays cancel/removed'''
obj_res_leave = self.pool.get('resource.calendar.leaves')
leave_ids = obj_res_leave.search(cr, uid, [('holiday_id', 'in', ids)], context=context)
- return obj_res_leave.unlink(cr, uid, leave_ids)
+ return obj_res_leave.unlink(cr, uid, leave_ids, context=context)
def onchange_type(self, cr, uid, ids, holiday_type):
result = {'value': {'employee_id': False}}
@@ -259,6 +269,7 @@ class hr_holidays(osv.osv):
'date_deadline': record.date_to,
}
case_id = meeting_obj.create(cr, uid, vals)
+ self._create_resource_leave(cr, uid, [record], context=context)
self.write(cr, uid, ids, {'case_id': case_id})
elif record.holiday_type == 'category':
emp_ids = obj_emp.search(cr, uid, [('category_ids', 'child_of', [record.category_id.id])])
@@ -313,6 +324,7 @@ class hr_holidays(osv.osv):
for request in record.linked_request_ids or []:
wf_service.trg_validate(uid, 'hr.holidays', request.id, 'cancel', cr)
+ self._remove_resource_leave(cr, uid, ids, context=context)
return True
def check_holidays(self, cr, uid, ids, context=None):
diff --git a/addons/hr_holidays/test/test_hr_holiday.yml b/addons/hr_holidays/test/test_hr_holiday.yml
index c4a847640b6..af476386e0b 100644
--- a/addons/hr_holidays/test/test_hr_holiday.yml
+++ b/addons/hr_holidays/test/test_hr_holiday.yml
@@ -1,140 +1,34 @@
-
In order to test the hr_holiday module in OpenERP, I will Allocate leaves for Employee and manage leaves and leaves requests.
--
- I create new user "test_holiday_user1" who will make leave request.
--
- !record {model: res.users, id: test_holiday_user1}:
- company_id: base.main_company
- context_lang: en_US
- groups_id:
- - base.group_user
- - base.group_hr_manager
- - base.group_hr_user
- login: test_holiday_user1
- name: test_holiday_user1
- password: test_holiday_user1
--
- I create a new employee “Mark Johnshon” as Manager to validate employee leave.
--
- !record {model: hr.employee, id: hr_employee_employee0}:
- address_home_id: base.res_partner_address_1
- company_id: base.main_company
- gender: male
- name: Mark Johnson
- user_id: base.user_root
--
- I Create another employee "Phil Graves" as "test_holiday_user1".
--
- !record {model: hr.employee, id: hr_employee_philgraves0}:
- address_home_id: base.res_partner_address_8
- name: Phil Graves
- parent_id: 'hr_employee_employee0'
- user_id: 'test_holiday_user1'
--
- I create a new Leave type "Sick Leave".
--
- !record {model: hr.holidays.status, id: hr_holidays_status_sick0}:
- color_name: red
- name: Sick Leave.
- limit: 12
-
--
- I allocate leave request for employee "Phil Graves".
--
- !record {model: hr.holidays, id: hr_holidays_allocateleaveforuser0}:
- holiday_status_id: hr_holidays_status_sick0
- name: Sick Leaves for Phil Graves
- number_of_days_temp: 12.0
- date_from: !eval "'%s-05-20 13:59:00' %(datetime.now().year)"
- date_to: !eval "'%s-05-22 13:59:00' %(datetime.now().year)"
- type: add
--
- I assign allocation type as 'Employee'.
--
- !python {model: hr.holidays}: |
- self.onchange_type(cr, uid, [ref('hr_holidays_allocateleaveforuser0')], 'employee')
--
- I change Leave Type of the employee.
--
- !python {model: hr.holidays}: |
- res = self.onchange_sec_id(cr, uid, [ref('hr_holidays_allocateleaveforuser0')], ref('holiday_status_sl'))
- values = dict([('holiday_status_id', ref('holiday_status_sl'))] + res['value'].items())
- self.write(cr, uid, [ref('hr_holidays_allocateleaveforuser0')], values, None)
-
I assign the dates in the holiday request.
-
- !python {model: hr.holidays}: |
- from datetime import datetime
- date_from = '%s-%s-%s 14:00:00' %(datetime.now().year, datetime.now().month, datetime.now().day)
- date_to = '%s-%s-%s 19:59:00' %(datetime.now().year, datetime.now().month, datetime.now().day)
- res = self.onchange_date_from(cr, uid, [ref('hr_holidays_allocateleaveforuser0')], date_to, date_from)
- values = dict([('date_from', date_from),('date_to',date_to)] + res['value'].items())
- self.write(cr, uid, [ref('hr_holidays_allocateleaveforuser0')], values, None)
+ !record {model: hr.holidays, id: hr_holiday1}:
+ name: Sick Leave
+ holiday_status_id: holiday_status_cl
+ date_from: !eval time.strftime('%Y-%m-10 10:00:00')
+ date_to: !eval time.strftime('%Y-%m-11 19:00:00')
+ employee_id: hr.employee1
+ type: remove
-
- I confirmed the allocation by clicking on "Confirm" button.
+ I confirmed the holiday request by clicking on "Confirm" button.
-
- !workflow {model: hr.holidays, action: confirm, ref: hr_holidays_allocateleaveforuser0}
-
+ !workflow {model: hr.holidays, action: confirm, ref: hr_holidays_employee1_cl}
-
- I find a small mistake on my leave request to I refuse the leave request to correct a mistake.
+ I find a small mistake on my leave request to I click on "Refuse" button to correct a mistake.
-
- !workflow {model: hr.holidays, action: refuse, ref: hr_holidays_allocateleaveforuser0}
-
+ !workflow {model: hr.holidays, action: refuse, ref: hr_holidays_employee1_cl}
-
I again set to draft and then confirm.
-
!python {model: hr.holidays}: |
import netsvc
wf_service = netsvc.LocalService("workflow")
- self.set_to_draft(cr, uid, [ref('hr_holidays_allocateleaveforuser0')])
- wf_service.trg_validate(uid, 'hr.holidays', ref('hr_holidays_allocateleaveforuser0'), 'confirm', cr)
+ self.set_to_draft(cr, uid, [ref('hr_holidays_employee1_cl')])
+ wf_service.trg_validate(uid, 'hr.holidays', ref('hr_holidays_employee1_cl'), 'confirm', cr)
-
- I validate the allocation by clicking on "To Approve" button.
+ I validate the holiday request by clicking on "To Approve" button.
-
- !workflow {model: hr.holidays, action: validate, ref: hr_holidays_allocateleaveforuser0}
-
+ !workflow {model: hr.holidays, action: validate, ref: hr_holidays_employee1_cl}
-
- I connect as "test_holiday_user1", and create a new leave request for employee "Phil Graves".
--
- !record {model: hr.holidays, id: hr_holidays_iwanttoleaveforgotohospital0}:
- date_from: !eval "'%s-05-20 11:48:00' %(datetime.now().year)"
- date_to: !eval "'%s-05-21 11:48:00' %(datetime.now().year)"
- employee_id: 'hr_employee_philgraves0'
- holiday_status_id: 'hr_holidays_status_sick0'
- name: Appointment with Doctor
- notes: My appointment with the doctor is confirmed. So please accept my leave.
- number_of_days_temp: 2.0
- type: remove
- user_id: 'test_holiday_user1'
--
- I check that Leave Request is in "Draft" state.
--
- !assert {model: hr.holidays, id: hr_holidays_iwanttoleaveforgotohospital0}:
- - state == 'draft'
-
--
- I confirm leave Request by click on "Confirm" button.
--
- !workflow {model: hr.holidays, action: confirm, ref: hr_holidays_iwanttoleaveforgotohospital0}
-
--
- I connect as "Admin" user and Open Leave request of "Phil Graves" and "validate" it by click on "Approve" button.
--
- !workflow {model: hr.holidays, action: validate, ref: hr_holidays_iwanttoleaveforgotohospital0}
-
--
- I check that "Leave Manager" field is automatically filled by the user who validate the leave'.
--
- !python {model: hr.holidays}: |
- ids2 = self.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
- obj = self.browse(cr, uid, ref('hr_holidays_iwanttoleaveforgotohospital0'))
- assert ids2[0] == obj.manager_id.id
-
--
- I check that Leave request state is "Validated".
--
- !assert {model: hr.holidays, id: hr_holidays_iwanttoleaveforgotohospital0}:
- - state == "validate"
-
--
- I can also see Summary of Employee's holiday by using 'Employee's Holidays' Report. This report will allows to choose to print holidays with state Confirmed, Validated or both.
+ I can also see Summary of Employee's holiday by using "Employee's Holidays" Report. This report will allows to choose to print holidays with state Confirmed, Validated or both.
diff --git a/addons/hr_holidays/wizard/hr_holidays_summary_employees_view.xml b/addons/hr_holidays/wizard/hr_holidays_summary_employees_view.xml
index 980ad38c855..530d2a60351 100644
--- a/addons/hr_holidays/wizard/hr_holidays_summary_employees_view.xml
+++ b/addons/hr_holidays/wizard/hr_holidays_summary_employees_view.xml
@@ -35,7 +35,6 @@
-
Employee's Holidays
client_print_multi
@@ -44,4 +43,4 @@
-
\ No newline at end of file
+
diff --git a/addons/hr_payroll/hr_payroll.py b/addons/hr_payroll/hr_payroll.py
index 080b0dede20..affd81cce6d 100644
--- a/addons/hr_payroll/hr_payroll.py
+++ b/addons/hr_payroll/hr_payroll.py
@@ -646,7 +646,7 @@ class hr_payslip(osv.osv):
'struct_id': False,
}
}
- if not employee_id:
+ if (not employee_id) or (not date_from) or (not date_to):
return res
ttyme = datetime.fromtimestamp(time.mktime(time.strptime(date_from, "%Y-%m-%d")))
employee_id = empolyee_obj.browse(cr, uid, employee_id, context=context)
@@ -928,6 +928,10 @@ class hr_payslip_line(osv.osv):
'total': fields.function(_calculate_total, method=True, type='float', string='Total', digits_compute=dp.get_precision('Payroll'),store=True ),
}
+ _defaults = {
+ 'quantity': 1.0,
+ }
+
hr_payslip_line()
class hr_payroll_structure(osv.osv):
diff --git a/addons/hr_payroll/test/payslip.yml b/addons/hr_payroll/test/payslip.yml
index 09af98ee0cd..94c38b263fc 100644
--- a/addons/hr_payroll/test/payslip.yml
+++ b/addons/hr_payroll/test/payslip.yml
@@ -40,25 +40,6 @@
-
!record {model: hr.payslip, id: hr_payslip_0}:
employee_id: hr_payroll.hr_employee_richard0
--
- The selection of an employee fills the contract, structure, worked days and input data fields
--
- !python {model: hr.payslip}: |
- import time
- from datetime import datetime
- from dateutil import relativedelta
- date_from = time.strftime('%Y-%m-01')
- worked_days_obj = self.pool.get('hr.payslip.worked_days')
- date_to = str(datetime.now() + relativedelta.relativedelta(months=+1, day=1, days=-1))[:10]
- res = self.onchange_employee_id(cr, uid, [], date_from, date_to, ref("hr_employee_richard0"), False, None)
- vals = {
- 'struct_id': res['value']['struct_id'],
- 'contract_id': res['value']['contract_id'],
- 'name': res['value']['name'],
- }
- vals['worked_days_line_ids'] = [(0,0,i) for i in res['value']['worked_days_line_ids']]
- vals['input_line_ids'] = [(0, 0, i) for i in res['value']['input_line_ids']]
- self.write(cr, uid, ref("hr_payslip_0"), vals)
-
I assign the amount to Input data
-
@@ -69,9 +50,8 @@
I verify the payslip is in draft state
-
!python {model: hr.payslip}: |
- from tools.translate import _
payslip_brw=self.browse(cr, uid, ref("hr_payslip_0"))
- assert(payslip_brw.state == 'draft'), _('State not changed!')
+ assert (payslip_brw.state == 'draft'), 'State not changed!'
-
I click on 'Compute Sheet' button on payslip
-
diff --git a/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.xml b/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.xml
index cb787cf536e..e48a8f6470c 100644
--- a/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.xml
+++ b/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.xml
@@ -33,7 +33,6 @@
-
PaySlip Lines
client_print_multi
@@ -42,4 +41,4 @@
-
\ No newline at end of file
+
diff --git a/addons/hr_payroll_account/hr_payroll_account.py b/addons/hr_payroll_account/hr_payroll_account.py
index 684289c9737..3463713d37d 100644
--- a/addons/hr_payroll_account/hr_payroll_account.py
+++ b/addons/hr_payroll_account/hr_payroll_account.py
@@ -39,7 +39,13 @@ class hr_payslip(osv.osv):
'journal_id': fields.many2one('account.journal', 'Expense Journal',states={'draft': [('readonly', False)]}, readonly=True, required=True),
'move_id': fields.many2one('account.move', 'Accounting Entry', readonly=True),
}
-
+
+ def copy(self, cr, uid, id, default=None, context=None):
+ if default is None:
+ default = {}
+ default['move_id'] = False
+ return super(hr_payslip, self).copy(cr, uid, id, default, context=context)
+
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
@@ -95,9 +101,9 @@ class hr_payslip(osv.osv):
partner_id = False
debit_account_id = line.salary_rule_id.account_debit.id
credit_account_id = line.salary_rule_id.account_credit.id
-
+
if debit_account_id:
-
+
debit_line = (0, 0, {
'name': line.name,
'date': timenow,
@@ -113,9 +119,9 @@ class hr_payslip(osv.osv):
})
line_ids.append(debit_line)
debit_sum += debit_line[2]['debit'] - debit_line[2]['credit']
-
+
if credit_account_id:
-
+
credit_line = (0, 0, {
'name': line.name,
'date': timenow,
@@ -147,7 +153,7 @@ class hr_payslip(osv.osv):
'credit': debit_sum - credit_sum,
})
line_ids.append(adjust_credit)
-
+
elif debit_sum < credit_sum:
acc_id = slip.journal_id.default_debit_account_id.id
if not acc_id:
diff --git a/addons/hr_recruitment/__openerp__.py b/addons/hr_recruitment/__openerp__.py
index b728609c53d..4797ef14044 100644
--- a/addons/hr_recruitment/__openerp__.py
+++ b/addons/hr_recruitment/__openerp__.py
@@ -54,7 +54,7 @@ system to store and search in your CV base.
'hr_recruitment_data.xml'
],
'demo_xml': [
- 'hr_recruitment_demo.xml',
+ 'hr_recruitment_demo.yml',
],
'test':['test/test_hr_recruitment.yml'],
'installable': True,
diff --git a/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml b/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml
index 822a7da3249..8af4b0f8553 100644
--- a/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml
+++ b/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml
@@ -33,8 +33,8 @@
form
-
-
+
+
diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py
index 931600fef9f..8acb95e7088 100644
--- a/addons/hr_recruitment/hr_recruitment.py
+++ b/addons/hr_recruitment/hr_recruitment.py
@@ -197,11 +197,19 @@ class hr_applicant(crm.crm_case, osv.osv):
'color': 0,
}
- def _read_group_stage_ids(self, cr, uid, ids, domain, context=None):
- context = context or {}
+ def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
+ access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('hr.recruitment.stage')
- stage_ids = stage_obj.search(cr, uid, ['|',('id','in',ids), ('department_id','=',False)], context=context)
- return stage_obj.name_get(cr, uid, stage_ids, context=context)
+ order = stage_obj._order
+ if read_group_order == 'stage_id desc':
+ # lame hack to allow reverting search, should just work in the trivial case
+ order = "%s desc" % order
+ stage_ids = stage_obj._search(cr, uid, ['|',('id','in',ids),('department_id','=',False)], order=order,
+ access_rights_uid=access_rights_uid, context=context)
+ result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
+ # restore order of the search
+ result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
+ return result
_group_by_full = {
'stage_id': _read_group_stage_ids
@@ -440,7 +448,18 @@ class hr_applicant(crm.crm_case, osv.osv):
})
else:
raise osv.except_osv(_('Warning!'),_('You must define Applied Job for Applicant !'))
- return self.case_close(cr, uid, ids, *args)
+ self.case_close(cr, uid, ids, *args)
+
+ mod_obj = self.pool.get('ir.model.data')
+ act = mod_obj.get_object_reference(cr, uid, 'hr', 'open_view_employee_list')
+
+ act_obj = self.pool.get('ir.actions.act_window')
+ act_win = act_obj.read(cr, uid, act[1], [])
+
+ act_win['domain'] = [('id','=',emp_id)]
+ act_win['view_mode'] = 'form,tree'
+ act_win['res_id'] = emp_id
+ return act_win
def case_reset(self, cr, uid, ids, *args):
"""Resets case as draft
diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml
index e1172f0b075..d6cb5952d38 100644
--- a/addons/hr_recruitment/hr_recruitment_data.xml
+++ b/addons/hr_recruitment/hr_recruitment_data.xml
@@ -19,19 +19,6 @@
-
-
-
-
- Salesman
-
-
-
-
- Junior Developer
-
-
-
diff --git a/addons/hr_recruitment/hr_recruitment_demo.xml b/addons/hr_recruitment/hr_recruitment_demo.xml
deleted file mode 100644
index 0fc2c94f034..00000000000
--- a/addons/hr_recruitment/hr_recruitment_demo.xml
+++ /dev/null
@@ -1,178 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/hr_recruitment/hr_recruitment_demo.yml b/addons/hr_recruitment/hr_recruitment_demo.yml
new file mode 100644
index 00000000000..bcf2248fc40
--- /dev/null
+++ b/addons/hr_recruitment/hr_recruitment_demo.yml
@@ -0,0 +1,109 @@
+-
+ !record {model: hr.applicant, id: hr_case_salesman0}:
+ date: !eval time.strftime('%Y-%m-01 10:35:50')
+ type_id: degree_graduate
+ priority: '2'
+ partner_name: 'Enrique Jones'
+ partner_mobile: '9963214587'
+ job_id: hr.job_jr_appli
+ stage_id: stage_job1
+ name: 'Salesman'
+ partner_phone: '1236547890'
+
+-
+ !record {model: hr.applicant, id: hr_case_traineemca0}:
+ partner_address_id: base.res_partner_address_14
+ date: !eval time.strftime('%Y-%m-10 18:15:00')
+ type_id: degree_bac5
+ priority: '3'
+ user_id: base.user_demo
+ partner_name: 'Marie Justine'
+ state: 'open'
+ partner_mobile: '9988774455'
+ job_id: hr.job_jr_appli
+ stage_id: stage_job4
+ name: 'Trainee - MCA'
+ partner_phone: '6633225'
+-
+ !record {model: hr.applicant, id: hr_case_fresher0}:
+ date: !eval time.strftime('%Y-%m-15 16:10:00')
+ type_id: degree_licenced
+ priority: '1'
+ user_id: base.user_root
+ partner_name: 'Jose'
+ job_id: hr.job_jr_appli
+ stage_id: stage_job3
+ name: 'Fresher'
+ partner_phone: '999666735'
+-
+ !record {model: hr.applicant, id: hr_case_yrsexperienceinphp0}:
+ date: !eval time.strftime('%Y-%m-25 16:25:52')
+ job_id: hr.job_jr_appli
+ type_id: degree_bac5
+ user_id: base.user_root
+ partner_name: 'Sandra Elvis'
+ state: 'cancel'
+ stage_id: stage_job6
+ name: 'More than 5 yrs Experience in PHP'
+-
+ !record {model: hr.applicant, id: hr_case_marketingjob0}:
+ date: !eval time.strftime('%Y-%m-26 17:15:32')
+ type_id: degree_licenced
+ user_id: base.user_demo
+ partner_name: 'John Bruno'
+ state: 'done'
+ job_id: hr.job_jr_appli
+ stage_id: stage_job5
+ name: 'Marketing Job'
+-
+ !record {model: hr.applicant, id: hr_case_financejob0}:
+ date: !eval time.strftime('%Y-%m-26 17:39:42')
+ type_id: degree_licenced
+ priority: '4'
+ partner_name: 'David Armstrong'
+ state: 'pending'
+ state: open
+ partner_mobile: '9966332214'
+ job_id: hr.job_jr_appli
+ stage_id: stage_job2
+ name: 'Finance Job'
+ partner_phone: '33968745'
+-
+ !record {model: hr.applicant, id: hr_case_traineemca1}:
+ partner_address_id: base.res_partner_address_14
+ date: !eval time.strftime('%Y-%m-12 17:49:19')
+ type_id: degree_bac5
+ partner_name: 'Tina Augustie'
+ state: 'open'
+ partner_mobile: '9898745745'
+ job_id: hr.job_jr_appli
+ stage_id: stage_job4
+ name: 'Trainee - MCA'
+ partner_phone: '6630125'
+
+-
+ !record {model: hr.applicant, id: hr_case_programmer}:
+ partner_address_id: base.res_partner_address_14
+ date: !eval time.strftime('%Y-%m-12 17:49:19')
+ type_id: degree_bac5
+ partner_name: 'Shane Williams'
+ state: 'open'
+ partner_mobile: '9812398524'
+ stage_id: stage_job4
+ name: 'Programmer'
+ partner_phone: '6630125'
+ job_id: hr.job_jr_appli
+ department_id: hr.dep_it
+ salary_expected: 11000.0
+
+-
+ !record {model: hr.applicant, id: hr_case_advertisement}:
+ date: !eval time.strftime('%Y-%m-26 17:39:42')
+ type_id: degree_licenced
+ partner_id: base.res_partner_11
+ partner_name: 'David Armstrong'
+ job_id: hr.job_jr_appli
+ stage_id: stage_job2
+ name: 'Advertisement'
+ salary_expected: 11000.0
+
diff --git a/addons/hr_recruitment/report/hr_recruitment_report_view.xml b/addons/hr_recruitment/report/hr_recruitment_report_view.xml
index 5353a8497ed..07f160b8ee2 100644
--- a/addons/hr_recruitment/report/hr_recruitment_report_view.xml
+++ b/addons/hr_recruitment/report/hr_recruitment_report_view.xml
@@ -50,9 +50,10 @@
-
+
hr.recruitment.report
form
tree,graph
- {'search_default_month':1,'search_default_department':1,'group_by_no_leaf':1,'group_by':[]}
+ {'search_default_year':1,'search_default_month':1,'search_default_department':1,'group_by_no_leaf':1,'group_by':[]}
diff --git a/addons/hr_recruitment/wizard/hr_recruitment_employee_hired.py b/addons/hr_recruitment/wizard/hr_recruitment_employee_hired.py
index d11a1f16775..291c16a7644 100644
--- a/addons/hr_recruitment/wizard/hr_recruitment_employee_hired.py
+++ b/addons/hr_recruitment/wizard/hr_recruitment_employee_hired.py
@@ -48,8 +48,7 @@ class hired_employee(osv.osv_memory):
"""
if context is None:
context = {}
- self.pool.get('hr.applicant').case_close_with_emp(cr, uid,context.get('active_ids', []))
- return {}
+ return self.pool.get('hr.applicant').case_close_with_emp(cr, uid,context.get('active_ids', []))
hired_employee()
diff --git a/addons/hr_timesheet/hr_timesheet_data.xml b/addons/hr_timesheet/hr_timesheet_data.xml
index 5dc95cf9ddc..e9aa56d4646 100644
--- a/addons/hr_timesheet/hr_timesheet_data.xml
+++ b/addons/hr_timesheet/hr_timesheet_data.xml
@@ -2,9 +2,6 @@
-
-
-
TS
@@ -12,5 +9,10 @@
general
+
+
+
+
+
diff --git a/addons/hr_timesheet_invoice/board_hr_timesheet_invoice.xml b/addons/hr_timesheet_invoice/board_hr_timesheet_invoice.xml
index 96d91ef3e5c..7327ca66f63 100644
--- a/addons/hr_timesheet_invoice/board_hr_timesheet_invoice.xml
+++ b/addons/hr_timesheet_invoice/board_hr_timesheet_invoice.xml
@@ -17,9 +17,9 @@
form
-
-
-
+
+
+
-->
@@ -31,8 +31,8 @@
form
-
-
+
+
-->
@@ -67,8 +67,8 @@
form
-
-
+
+
diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
index 87fb6064a04..19d08f7faf3 100644
--- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
+++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
@@ -63,12 +63,12 @@ class account_analytic_account(osv.osv):
_inherit = "account.analytic.account"
_columns = {
- 'pricelist_id': fields.many2one('product.pricelist', 'Sale Pricelist',
+ 'pricelist_id': fields.many2one('product.pricelist', 'Customer Pricelist',
help="The product to invoice is defined on the employee form, the price will be deduced by this pricelist on the product."),
'amount_max': fields.float('Max. Invoice Price'),
'amount_invoiced': fields.function(_invoiced_calc, string='Invoiced Amount',
help="Total invoiced"),
- 'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Reinvoice Costs',
+ 'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Invoice on Timesheet & Costs',
help="Fill this field if you plan to automatically generate invoices based " \
"on the costs in this analytic account: timesheets, expenses, ..." \
"You can configure an automatic invoice rate on analytic accounts."),
@@ -76,7 +76,16 @@ class account_analytic_account(osv.osv):
_defaults = {
'pricelist_id': lambda self, cr, uid, ctx: ctx.get('pricelist_id', False),
}
-
+ def on_change_partner_id(self, cr, uid, id, partner_id, context={}):
+ res = super(account_analytic_account, self).on_change_partner_id(cr, uid, id, partner_id, context)
+ if (not res.get('value', False)) or not partner_id:
+ return res
+ part = self.pool.get('res.partner').browse(cr, uid, partner_id)
+ pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
+ if pricelist:
+ res['value']['pricelist_id'] = pricelist
+ return res
+
def set_close(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state':'close'}, context=context)
diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml b/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml
index 2ce309e0df4..e0652355063 100644
--- a/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml
+++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml
@@ -7,23 +7,27 @@
form
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -123,7 +127,7 @@
tree,form
[]
{'search_default_to_invoice': 1}
-
+
This list shows you every task you can invoice to the customer. Select the lines and click the Action button to generate the invoices automatically.
+ context="{'search_default_account_id': [active_id]}"
+ id="act_account_analytic_account_2_report_timehsheet_account"
+ name="Timesheets"
+ res_model="hr.analytic.timesheet"
+ src_model="account.analytic.account"/>
', False), ('account_id', 'in', context['active_ids'])], context=context)
+ invs = self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, ids, data, context=context)
mod_obj = self.pool.get('ir.model.data')
- analytic_account_obj = self.pool.get('account.analytic.account')
- res_partner_obj = self.pool.get('res.partner')
- account_payment_term_obj = self.pool.get('account.payment.term')
- invoice_obj = self.pool.get('account.invoice')
- product_obj = self.pool.get('product.product')
- fiscal_pos_obj = self.pool.get('account.fiscal.position')
- invoice_line_obj = self.pool.get('account.invoice.line')
- invoices = []
-
- if context is None:
- context = {}
- result = mod_obj._get_id(cr, uid, 'account', 'view_account_invoice_filter')
- res = mod_obj.read(cr, uid, result, ['res_id'], context=context)
-
- data = self.browse(cr, uid, ids, context=context)[0]
- balance_product = data.balance_product.id
-
- account_ids = 'active_ids' in context and context['active_ids'] or []
-
- for account in analytic_account_obj.browse(cr, uid, account_ids, context=context):
- partner = account.partner_id
- amount_total=0.0
- if (not partner) or not (account.pricelist_id):
- raise osv.except_osv(_('Analytic account incomplete'),
- _('Please fill in the partner and pricelist field '
- 'in the analytic account:\n%s') % (account.name,))
-
- date_due = False
- if partner.property_payment_term:
- pterm_list= account_payment_term_obj.compute(cr, uid,
- partner.property_payment_term.id, value=1,
- date_ref=time.strftime('%Y-%m-%d'))
- if pterm_list:
- pterm_list = [line[0] for line in pterm_list]
- pterm_list.sort()
- date_due = pterm_list[-1]
-
- curr_invoice = {
- 'name': time.strftime('%d/%m/%Y')+' - '+account.name,
- 'partner_id': account.partner_id.id,
- 'address_contact_id': res_partner_obj.address_get(cr, uid, [account.partner_id.id], adr_pref=['contact'])['contact'],
- 'address_invoice_id': res_partner_obj.address_get(cr, uid, [account.partner_id.id], adr_pref=['invoice'])['invoice'],
- 'payment_term': partner.property_payment_term.id or False,
- 'account_id': partner.property_account_receivable.id,
- 'currency_id': account.pricelist_id.currency_id.id,
- 'date_due': date_due,
- 'fiscal_position': account.partner_id.property_account_position.id
- }
- last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context)
- invoices.append(last_invoice)
-
- context2 = context.copy()
- context2['lang'] = partner.lang
-
- cr.execute("""SELECT
- line.product_id,
- sum(line.amount),
- line.general_account_id,
- line.product_uom_id,
- move_line.ref
- FROM
- account_analytic_line as line
- LEFT JOIN account_move_line as move_line on (line.move_id=move_line.id)
- LEFT JOIN account_analytic_journal as journal on (line.journal_id=journal.id)
- WHERE
- line.account_id = %s AND
- line.move_id IS NOT NULL AND
- journal.type = 'sale'
- GROUP BY
- line.product_id,
- line.general_account_id,
- line.product_uom_id,
- move_line.ref""", (account.id,))
- for product_id, amount, account_id, product_uom_id, ref in cr.fetchall():
- product = product_obj.browse(cr, uid, product_id, context2)
-
- if product:
- taxes = product.taxes_id
- else:
- taxes = []
-
- tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
- if not account_id:
- raise osv.except_osv(_("Configuration Error"), _("No income account defined for product '%s'") % product.name)
- curr_line = {
- 'price_unit': -amount,
- 'quantity': 1.0,
- 'discount': 0.0,
- 'invoice_line_tax_id': [(6,0,tax)],
- 'invoice_id': last_invoice,
- 'name': ref or '' +(product and ' - '+product.name or ''),
- 'product_id': product_id,
- 'uos_id': product_uom_id,
- 'account_id': account_id,
- 'account_analytic_id': account.id
- }
- invoice_line_obj.create(cr, uid, curr_line, context=context)
-
- if not balance_product:
- raise osv.except_osv(_('Balance product needed'), _('Please fill a Balance product in the wizard'))
- product = product_obj.browse(cr, uid, balance_product, context=context2)
- taxes = product.taxes_id
- tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
- account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id
- if not account_id:
- raise osv.except_osv(_("Configuration Error"), _("No income account defined for product '%s'") % product.name)
- curr_line = {
- 'price_unit': account.amount_max - amount_total,
- 'quantity': 1.0,
- 'discount': 0.0,
- 'invoice_line_tax_id': [(6,0,tax)],
- 'invoice_id': last_invoice,
- 'name': product.name,
- 'product_id': product.id,
- 'uos_id': product.uom_id.id,
- 'account_id': account_id,
- 'account_analytic_id': account.id
- }
- invoice_line_obj.create(cr, uid, curr_line, context=context)
- if account.amount_max < amount_total:
- invoice_obj.write(cr, uid, [last_invoice], {'type': 'out_refund',}, context=context)
- cr.execute('update account_analytic_line set invoice_id=%s where invoice_id is null and account_id=%s', (last_invoice, account.id))
-
- return {
- 'domain': "[('id','in', ["+','.join(map(str,invoices))+"])]",
- 'name': 'Invoices',
- 'view_type': 'form',
- 'view_mode': 'tree,form',
- 'res_model': 'account.invoice',
- 'view_id': False,
- 'context': "{'type':'out_invoice'}",
- 'type': 'ir.actions.act_window',
- 'search_view_id': res['res_id']
- }
+ act_obj = self.pool.get('ir.actions.act_window')
+ mod_ids = mod_obj.search(cr, uid, [('name', '=', 'action_invoice_tree1')], context=context)[0]
+ res_id = mod_obj.read(cr, uid, mod_ids, ['res_id'], context=context)['res_id']
+ act_win = act_obj.read(cr, uid, res_id, [], context=context)
+ act_win['domain'] = [('id','in',invs),('type','=','out_invoice')]
+ act_win['name'] = _('Invoices')
+ return act_win
final_invoice_create()
diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py
index 502d35ff2d1..134bde8a872 100644
--- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py
+++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py
@@ -27,43 +27,18 @@ from tools.translate import _
## Create an invoice based on selected timesheet lines
#
-#
-# TODO: check unit of measure !!!
-#
-class hr_timesheet_invoice_create(osv.osv_memory):
+class account_analytic_line(osv.osv):
+ _inherit = "account.analytic.line"
- _name = 'hr.timesheet.invoice.create'
- _description = 'Create invoice from timesheet'
- _columns = {
- 'date': fields.boolean('Date', help='The real date of each work will be displayed on the invoice'),
- 'time': fields.boolean('Time spent', help='The time of each work done will be displayed on the invoice'),
- 'name': fields.boolean('Description', help='The detail of each work done will be displayed on the invoice'),
- 'price': fields.boolean('Cost', help='The cost of each work done will be displayed on the invoice. You probably don\'t want to check this'),
- 'product': fields.many2one('product.product', 'Product', help='Complete this field only if you want to force to use a specific product. Keep empty to use the real product that comes from the cost.'),
- }
-
- _defaults = {
- 'date': lambda *args: 1,
- 'name': lambda *args: 1
- }
-
- def view_init(self, cr, uid, fields, context=None):
- """
- This function checks for precondition before wizard executes
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param fields: List of fields for default value
- @param context: A standard dictionary for contextual values
- """
- analytic_obj = self.pool.get('account.analytic.line')
- data = context and context.get('active_ids', [])
- for analytic in analytic_obj.browse(cr, uid, data, context=context):
- if analytic.invoice_id:
- raise osv.except_osv(_('Warning !'), _("Invoice is already linked to some of the analytic line(s)!"))
-
- def do_create(self, cr, uid, ids, context=None):
- mod_obj = self.pool.get('ir.model.data')
+ #
+ # data = {
+ # 'date': boolean
+ # 'time': boolean
+ # 'name': boolean
+ # 'price': boolean
+ # 'product': many2one id
+ # }
+ def invoice_cost_create(self, cr, uid, ids, data={}, context=None):
analytic_account_obj = self.pool.get('account.analytic.account')
res_partner_obj = self.pool.get('res.partner')
account_payment_term_obj = self.pool.get('account.payment.term')
@@ -77,11 +52,9 @@ class hr_timesheet_invoice_create(osv.osv_memory):
invoices = []
if context is None:
context = {}
- result = mod_obj._get_id(cr, uid, 'account', 'view_account_invoice_filter')
- data = self.read(cr, uid, ids, [], context=context)[0]
account_ids = {}
- for line in self.pool.get('account.analytic.line').browse(cr, uid, context['active_ids'], context=context):
+ for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context):
account_ids[line.account_id.id] = True
account_ids = account_ids.keys() #data['accounts']
@@ -127,7 +100,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
"FROM account_analytic_line as line " \
"WHERE account_id = %s " \
"AND id IN %s AND to_invoice IS NOT NULL " \
- "GROUP BY product_id,to_invoice,product_uom_id", (account.id, tuple(context['active_ids']),))
+ "GROUP BY product_id,to_invoice,product_uom_id", (account.id, tuple(ids),))
for product_id, factor_id, qty, uom in cr.fetchall():
product = product_obj.browse(cr, uid, product_id, context2)
@@ -135,7 +108,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
raise osv.except_osv(_('Error'), _('At least one line has no product !'))
factor_name = ''
factor = invoice_factor_obj.browse(cr, uid, factor_id, context2)
- if not data['product']:
+ if not data.get('product', False):
if factor.customer_name:
factor_name = product.name+' - '+factor.customer_name
else:
@@ -148,7 +121,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
ctx.update({'uom':uom})
if account.pricelist_id:
pl = account.pricelist_id.id
- price = pro_price_obj.price_get(cr,uid,[pl], data['product'] or product_id, qty or 1.0, account.partner_id.id, context=ctx)[pl]
+ price = pro_price_obj.price_get(cr,uid,[pl], data.get('product', False) or product_id, qty or 1.0, account.partner_id.id, context=ctx)[pl]
else:
price = 0.0
@@ -164,7 +137,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
'invoice_line_tax_id': [(6,0,tax )],
'invoice_id': last_invoice,
'name': factor_name,
- 'product_id': data['product'] or product_id,
+ 'product_id': data.get('product',product_id),
'invoice_line_tax_id': [(6,0,tax)],
'uos_id': uom,
'account_id': account_id,
@@ -174,37 +147,76 @@ class hr_timesheet_invoice_create(osv.osv_memory):
#
# Compute for lines
#
- cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(context['active_ids']), product_id, factor_id))
+ cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(ids), product_id, factor_id))
line_ids = cr.dictfetchall()
note = []
for line in line_ids:
# set invoice_line_note
details = []
- if data['date']:
+ if data.get('date', False):
details.append(line['date'])
- if data['time']:
+ if data.get('time', False):
if line['product_uom_id']:
details.append("%s %s" % (line['unit_amount'], product_uom_obj.browse(cr, uid, [line['product_uom_id']],context2)[0].name))
else:
details.append("%s" % (line['unit_amount'], ))
- if data['name']:
+ if data.get('name', False):
details.append(line['name'])
note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
curr_line['note'] = "\n".join(map(lambda x: unicode(x) or '',note))
invoice_line_obj.create(cr, uid, curr_line, context=context)
- cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(context['active_ids'])))
+ cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context)
+ return invoices
+#
+# TODO: check unit of measure !!!
+#
+
+class hr_timesheet_invoice_create(osv.osv_memory):
+
+ _name = 'hr.timesheet.invoice.create'
+ _description = 'Create invoice from timesheet'
+ _columns = {
+ 'date': fields.boolean('Date', help='The real date of each work will be displayed on the invoice'),
+ 'time': fields.boolean('Time spent', help='The time of each work done will be displayed on the invoice'),
+ 'name': fields.boolean('Description', help='The detail of each work done will be displayed on the invoice'),
+ 'price': fields.boolean('Cost', help='The cost of each work done will be displayed on the invoice. You probably don\'t want to check this'),
+ 'product': fields.many2one('product.product', 'Product', help='Complete this field only if you want to force to use a specific product. Keep empty to use the real product that comes from the cost.'),
+ }
+
+ _defaults = {
+ 'date': lambda *args: 1,
+ 'name': lambda *args: 1
+ }
+
+ def view_init(self, cr, uid, fields, context=None):
+ """
+ This function checks for precondition before wizard executes
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param fields: List of fields for default value
+ @param context: A standard dictionary for contextual values
+ """
+ analytic_obj = self.pool.get('account.analytic.line')
+ data = context and context.get('active_ids', [])
+ for analytic in analytic_obj.browse(cr, uid, data, context=context):
+ if analytic.invoice_id:
+ raise osv.except_osv(_('Warning !'), _("Invoice is already linked to some of the analytic line(s)!"))
+
+ def do_create(self, cr, uid, ids, context=None):
+ data = self.read(cr, uid, ids, [], context=context)[0]
+ invs = self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, context['active_ids'], data, context=context)
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
-
mod_ids = mod_obj.search(cr, uid, [('name', '=', 'action_invoice_tree1')], context=context)[0]
res_id = mod_obj.read(cr, uid, mod_ids, ['res_id'], context=context)['res_id']
act_win = act_obj.read(cr, uid, res_id, [], context=context)
- act_win['domain'] = [('id','in',invoices),('type','=','out_invoice')]
+ act_win['domain'] = [('id','in',invs),('type','=','out_invoice')]
act_win['name'] = _('Invoices')
return act_win
diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_final_view.xml b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_final_view.xml
index b45deeae8a2..d366e795c15 100644
--- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_final_view.xml
+++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_final_view.xml
@@ -1,49 +1,50 @@
-
-
- hr.timesheet.invoice.create.final.form
- hr.timesheet.invoice.create.final
- form
-
-
-
-
-
- Final Invoice
+ Create Invoice
ir.actions.act_window
hr.timesheet.invoice.create.final
form
form
new
+
-
+
- Final Invoice
+ Invoice
client_action_multi
action
account.analytic.account
-
+
+
+ hr.timesheet.invoice.create.final.form
+ hr.timesheet.invoice.create.final
+ form
+
+
+
+
+
+
diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_view.xml b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_view.xml
index 1696710b712..60b60e88550 100644
--- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_view.xml
+++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_view.xml
@@ -43,7 +43,6 @@
-
Create Invoice
client_action_multi
diff --git a/addons/hr_timesheet_sheet/board_hr_timesheet_view.xml b/addons/hr_timesheet_sheet/board_hr_timesheet_view.xml
index fd9b27f42aa..39ab5fe3482 100644
--- a/addons/hr_timesheet_sheet/board_hr_timesheet_view.xml
+++ b/addons/hr_timesheet_sheet/board_hr_timesheet_view.xml
@@ -40,8 +40,8 @@
form
-
-
+
+
diff --git a/addons/hr_timesheet_sheet/report/hr_timesheet_report_view.xml b/addons/hr_timesheet_sheet/report/hr_timesheet_report_view.xml
index 96151786676..11915fb64c9 100644
--- a/addons/hr_timesheet_sheet/report/hr_timesheet_report_view.xml
+++ b/addons/hr_timesheet_sheet/report/hr_timesheet_report_view.xml
@@ -41,11 +41,12 @@
-
-
+
@@ -90,7 +91,7 @@
hr.timesheet.report
form
tree,graph
- {'search_default_month':1,'search_default_group_user_id':1,'group_by_no_leaf':1,'group_by':[]}
+ {'search_default_year':1,'search_default_month':1,'search_default_group_user_id':1,'group_by_no_leaf':1,'group_by':[]}
This report performs analysis on timesheets created by your human resources in the system. It allows you to have a full overview of entries done by your employees. You can group them by specific selection criteria thanks to the search tool.
-
+
timesheet.report
form
tree,graph
- {'search_default_month':1,'search_default_User_id':1,'group_by_no_leaf':1,'group_by':[]}
+ {'search_default_year':1,'search_default_month':1,'search_default_User_id':1,'group_by_no_leaf':1,'group_by':[]}
Ideas
-
diff --git a/addons/l10n_be/__openerp__.py b/addons/l10n_be/__openerp__.py
index 26c9837a8bf..d4300186660 100644
--- a/addons/l10n_be/__openerp__.py
+++ b/addons/l10n_be/__openerp__.py
@@ -42,27 +42,27 @@ Wizards provided by this module:
""",
'author': 'OpenERP SA',
'depends': [
- 'account',
- 'base_vat',
- 'base_iban',
- 'account_chart',
- 'account_coda',
- ],
+ 'account',
+ 'base_vat',
+ 'base_iban',
+ 'account_chart',
+ 'account_coda',
+ ],
'init_xml': [],
'update_xml': [
- 'account_pcmn_belgium.xml',
- 'account_tax_code_template.xml',
- 'account_chart_template.xml',
- 'account_tax_template.xml',
- 'l10n_be_wizard.xml',
- 'wizard/l10n_be_account_vat_declaration_view.xml',
- 'wizard/l10n_be_vat_intra_view.xml',
- 'wizard/l10n_be_partner_vat_listing.xml',
- 'l10n_be_sequence.xml',
- 'fiscal_templates.xml',
- 'account_fiscal_position_tax_template.xml',
- 'security/ir.model.access.csv'
- ],
+ 'account_pcmn_belgium.xml',
+ 'account_tax_code_template.xml',
+ 'account_chart_template.xml',
+ 'account_tax_template.xml',
+ 'wizard/l10n_be_account_vat_declaration_view.xml',
+ 'wizard/l10n_be_vat_intra_view.xml',
+ 'wizard/l10n_be_partner_vat_listing.xml',
+ 'l10n_be_sequence.xml',
+ 'fiscal_templates.xml',
+ 'account_fiscal_position_tax_template.xml',
+ 'security/ir.model.access.csv',
+ 'l10n_be_wizard.yml'
+ ],
'demo_xml': [],
'installable': True,
'certificate': '0031977724637',
diff --git a/addons/l10n_be/l10n_be_wizard.xml b/addons/l10n_be/l10n_be_wizard.xml
deleted file mode 100644
index 194e2881d1b..00000000000
--- a/addons/l10n_be/l10n_be_wizard.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
- automatic
-
-
-
-
diff --git a/addons/l10n_be/l10n_be_wizard.yml b/addons/l10n_be/l10n_be_wizard.yml
new file mode 100644
index 00000000000..a51d1d55244
--- /dev/null
+++ b/addons/l10n_be/l10n_be_wizard.yml
@@ -0,0 +1,16 @@
+-
+ !record {model: ir.actions.todo, id: config_call_account_template}:
+ action_id: account.action_wizard_multi_chart
+ category_id: account.category_accounting_configuration
+ type: automatic
+-
+ !python {model: ir.actions.todo}: |
+ if self.browse(cr, uid, ref('l10n_be.config_call_account_template')).state=='open':
+ wiz = self.pool.get('wizard.multi.charts.accounts')
+ wiz_id = wiz.create(cr, uid, {
+ 'chart_template_id': ref('l10n_be.l10nbe_chart_template')
+ })
+ wiz.execute(cr, uid, [wiz_id])
+ self.write(cr, uid, [ref('l10n_be.config_call_account_template')], {
+ 'state': 'done'
+ })
diff --git a/addons/l10n_br/i18n/oc.po b/addons/l10n_br/i18n/oc.po
new file mode 100644
index 00000000000..371c4f5adeb
--- /dev/null
+++ b/addons/l10n_br/i18n/oc.po
@@ -0,0 +1,36 @@
+# Occitan (post 1500) 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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-07 06:40+0000\n"
+"PO-Revision-Date: 2011-11-20 09:22+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Occitan (post 1500) \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-21 05:22+0000\n"
+"X-Generator: Launchpad (build 14299)\n"
+
+#. module: l10n_br
+#: model:ir.actions.todo,note:l10n_br.config_call_account_template_brazilian_localization
+msgid ""
+"Generate Chart of Accounts from a Chart Template. You will be asked to pass "
+"the name of the company, the chart template to follow, the no. of digits to "
+"generate the code for your accounts and Bank account, currency to create "
+"Journals. Thus,the pure copy of chart Template is generated.\n"
+" This is the same wizard that runs from Financial "
+"Management/Configuration/Financial Accounting/Financial Accounts/Generate "
+"Chart of Accounts from a Chart Template."
+msgstr ""
+
+#. module: l10n_br
+#: model:ir.module.module,description:l10n_br.module_meta_information
+#: model:ir.module.module,shortdesc:l10n_br.module_meta_information
+msgid "Brazilian Localization"
+msgstr "Localizacion brasilièra"
diff --git a/addons/l10n_ch/__openerp__.py b/addons/l10n_ch/__openerp__.py
index 1c6bb5688d7..cabd15e42de 100644
--- a/addons/l10n_ch/__openerp__.py
+++ b/addons/l10n_ch/__openerp__.py
@@ -63,7 +63,7 @@ TODO :
""",
"version" : "6.1",
"author" : "Camptocamp",
- "category" : "Finance",
+ 'category': 'Localization/Account Charts',
"website": "http://www.camptocamp.com",
"depends" : [
diff --git a/addons/l10n_ch/test/l10n_ch_dta.yml b/addons/l10n_ch/test/l10n_ch_dta.yml
index 3f1dbacfd86..5dc41adedd8 100644
--- a/addons/l10n_ch/test/l10n_ch_dta.yml
+++ b/addons/l10n_ch/test/l10n_ch_dta.yml
@@ -6,7 +6,7 @@
# Creating 1 invoice #
###########################
-
- !record {model: account.invoice, id: dta_account_invoice}:
+ !record {model: account.invoice, id: dta_account_invoice, view: False}:
company_id: base.main_company
journal_id: account.bank_journal
currency_id: base.EUR
@@ -25,7 +25,7 @@
-
I add an invoice line
-
- !record {model: account.invoice.line, id: dta_invoice_line}:
+ !record {model: account.invoice.line, id: dta_invoice_line, view: False}:
account_id: account.a_expense
name: '[PC1] Basic PC'
price_unit: 700.0
diff --git a/addons/l10n_ch/test/l10n_ch_report.yml b/addons/l10n_ch/test/l10n_ch_report.yml
index e5b570fe58f..f120b14f33b 100644
--- a/addons/l10n_ch/test/l10n_ch_report.yml
+++ b/addons/l10n_ch/test/l10n_ch_report.yml
@@ -35,7 +35,7 @@
-
In order to test the PDF BVR webkit reports defined on an invoice, we will create a Invoice Record
-
- !record {model: account.invoice, id: l10n_ch_invoice}:
+ !record {model: account.invoice, id: l10n_ch_invoice, view: False}:
currency_id: base.CHF
company_id: base.main_company
address_invoice_id: res_partner_address_2
@@ -49,7 +49,7 @@
-
In order to test the BVR report, I will assign a bank to the invoice
-
- !record {model: account.invoice, id: l10n_ch_invoice}:
+ !record {model: account.invoice, id: l10n_ch_invoice, view: False}:
partner_bank_id: main_partner_bank
-
In order to test the PDF reports defined on a l10n_ch, we will print BVR Report
@@ -57,4 +57,4 @@
!python {model: account.invoice}: |
from tools.test_reports import try_report
company = self.pool.get('res.users').browse(cr, uid, uid).company_id
- company.lib_path and try_report(cr, uid, 'report.invoice_web_bvr', [ref('l10n_ch_invoice')]) or 'Webkit lib not set'
\ No newline at end of file
+ company.lib_path and try_report(cr, uid, 'report.invoice_web_bvr', [ref('l10n_ch_invoice')]) or 'Webkit lib not set'
diff --git a/addons/l10n_ch/test/l10n_ch_v11.yml b/addons/l10n_ch/test/l10n_ch_v11.yml
index 35e9870fe69..e30012dd6b8 100644
--- a/addons/l10n_ch/test/l10n_ch_v11.yml
+++ b/addons/l10n_ch/test/l10n_ch_v11.yml
@@ -4,7 +4,7 @@
-
I create an invoice ref 1234567
-
- !record {model: account.invoice, id: v11_test_invoice}:
+ !record {model: account.invoice, id: v11_test_invoice, view: False}:
name: V11 YAML invoice
number: 1234567
company_id: base.main_company
@@ -26,7 +26,7 @@
-
I create an invoice line
-
- !record {model: account.invoice.line, id: v11_test_invoice_line}:
+ !record {model: account.invoice.line, id: v11_test_invoice_line, view: False}:
account_id: account.a_expense
name: '[PC1] Basic PC'
price_unit: 888.00
@@ -51,7 +51,7 @@
-
I create a bank statement
-
- !record {model: account.bank.statement, id: v11_test_bank_statement}:
+ !record {model: account.bank.statement, id: v11_test_bank_statement, view: False}:
#account_id:
#balance_end:
#balance_end_cash:
diff --git a/addons/l10n_ch/test/l10n_ch_v11_part.yml b/addons/l10n_ch/test/l10n_ch_v11_part.yml
index ea0b88eff79..af726ae97ea 100644
--- a/addons/l10n_ch/test/l10n_ch_v11_part.yml
+++ b/addons/l10n_ch/test/l10n_ch_v11_part.yml
@@ -5,7 +5,7 @@
-
I create an invoice ref 2000999 of EUR 250.00
-
- !record {model: account.invoice, id: v11_part_test_invoice}:
+ !record {model: account.invoice, id: v11_part_test_invoice, view: False}:
name: V11 YAML invoice
number: 2000999
company_id: base.main_company
@@ -27,7 +27,7 @@
-
I create an invoice line
-
- !record {model: account.invoice.line, id: v11_part_test_invoice_line}:
+ !record {model: account.invoice.line, id: v11_part_test_invoice_line, view: False}:
account_id: account.a_expense
name: '[PC1] Basic PC'
price_unit: 250.00
@@ -55,7 +55,7 @@
# Creating 1st bank statement #
#########################################
-
- !record {model: account.bank.statement, id: v11_part_test_bank_statement_1}:
+ !record {model: account.bank.statement, id: v11_part_test_bank_statement_1, view: False}:
#account_id:
#balance_end:
#balance_end_cash:
@@ -176,7 +176,7 @@
# Creating 2nd bank statement #
#########################################
-
- !record {model: account.bank.statement, id: v11_part_test_bank_statement_2}:
+ !record {model: account.bank.statement, id: v11_part_test_bank_statement_2, view: False}:
#account_id:
#balance_end:
#balance_end_cash:
diff --git a/addons/l10n_ch/wizard/create_dta_view.xml b/addons/l10n_ch/wizard/create_dta_view.xml
index 53d0cdc42d8..11cd2095e28 100644
--- a/addons/l10n_ch/wizard/create_dta_view.xml
+++ b/addons/l10n_ch/wizard/create_dta_view.xml
@@ -32,7 +32,6 @@
-
Create DTA
client_action_multi
@@ -40,4 +39,4 @@
payment.order
-
\ No newline at end of file
+
diff --git a/addons/l10n_es/i18n/oc.po b/addons/l10n_es/i18n/oc.po
new file mode 100644
index 00000000000..3abe8d9ae4a
--- /dev/null
+++ b/addons/l10n_es/i18n/oc.po
@@ -0,0 +1,39 @@
+# Occitan (post 1500) 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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-11 11:15+0000\n"
+"PO-Revision-Date: 2011-11-20 09:22+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Occitan (post 1500) \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-21 05:22+0000\n"
+"X-Generator: Launchpad (build 14299)\n"
+
+#. module: l10n_es
+#: model:ir.module.module,shortdesc:l10n_es.module_meta_information
+msgid "Spanish Charts of Accounts (PGCE 2008)"
+msgstr "Plan comptable espanhòl (PGCE 2008)"
+
+#. module: l10n_es
+#: model:ir.module.module,description:l10n_es.module_meta_information
+msgid ""
+"Spanish Charts of Accounts (PGCE 2008)\n"
+"\n"
+"* Defines the following chart of account templates:\n"
+" * Spanish General Chart of Accounts 2008.\n"
+" * Spanish General Chart of Accounts 2008 for small and medium "
+"companies.\n"
+"* Defines templates for sale and purchase VAT.\n"
+"* Defines tax code templates.\n"
+"\n"
+"Note: You should install the l10n_ES_account_balance_report module\n"
+"for yearly account reporting (balance, profit & losses).\n"
+msgstr ""
diff --git a/addons/l10n_fr/wizard/fr_report_bilan_view.xml b/addons/l10n_fr/wizard/fr_report_bilan_view.xml
index e8615935b8c..eb66de9f012 100644
--- a/addons/l10n_fr/wizard/fr_report_bilan_view.xml
+++ b/addons/l10n_fr/wizard/fr_report_bilan_view.xml
@@ -31,7 +31,6 @@
-
Bilan Report
client_print_multi
@@ -40,4 +39,4 @@
-
\ No newline at end of file
+
diff --git a/addons/l10n_fr/wizard/fr_report_compute_resultant_view.xml b/addons/l10n_fr/wizard/fr_report_compute_resultant_view.xml
index f50ec1ef308..5499e2fe1cf 100644
--- a/addons/l10n_fr/wizard/fr_report_compute_resultant_view.xml
+++ b/addons/l10n_fr/wizard/fr_report_compute_resultant_view.xml
@@ -30,7 +30,6 @@
-
Compte de resultat Report
client_print_multi
@@ -39,4 +38,4 @@
-
\ No newline at end of file
+
diff --git a/addons/l10n_gt/i18n/oc.po b/addons/l10n_gt/i18n/oc.po
new file mode 100644
index 00000000000..e077ec6c3c0
--- /dev/null
+++ b/addons/l10n_gt/i18n/oc.po
@@ -0,0 +1,39 @@
+# Occitan (post 1500) 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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-11 11:15+0000\n"
+"PO-Revision-Date: 2011-11-20 09:23+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Occitan (post 1500) \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-21 05:22+0000\n"
+"X-Generator: Launchpad (build 14299)\n"
+
+#. module: l10n_gt
+#: model:ir.module.module,shortdesc:l10n_gt.module_meta_information
+msgid "Guatemala - Plan contable general"
+msgstr "Guatemala - Plan comptable general"
+
+#. module: l10n_gt
+#: model:ir.actions.todo,note:l10n_gt.config_call_account_template_gt_minimal
+msgid ""
+"Generar la nomenclatura contable a partir de un modelo. Deberá seleccionar "
+"una compañía, el modelo a utilizar, el número de digitos a usar en la "
+"nomenclatura, la moneda para crear los diarios."
+msgstr ""
+
+#. module: l10n_gt
+#: model:ir.module.module,description:l10n_gt.module_meta_information
+msgid ""
+"Agrega una nomenclatura contable para Guatemala. También icluye impuestos y "
+"la moneda del Quetzal. -- Adds accounting chart for Guatemala. It also "
+"includes taxes and the Quetzal currency"
+msgstr ""
diff --git a/addons/l10n_in/i18n/oc.po b/addons/l10n_in/i18n/oc.po
new file mode 100644
index 00000000000..f52b722a33a
--- /dev/null
+++ b/addons/l10n_in/i18n/oc.po
@@ -0,0 +1,46 @@
+# Occitan (post 1500) 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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-11 11:15+0000\n"
+"PO-Revision-Date: 2011-11-20 09:23+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Occitan (post 1500) \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-21 05:22+0000\n"
+"X-Generator: Launchpad (build 14299)\n"
+
+#. module: l10n_in
+#: model:ir.module.module,description:l10n_in.module_meta_information
+msgid ""
+"\n"
+" Indian Accounting : chart of Account\n"
+" "
+msgstr ""
+"\n"
+" Comptabilitat indiana : plan de comptes\n"
+" "
+
+#. module: l10n_in
+#: model:ir.module.module,shortdesc:l10n_in.module_meta_information
+msgid "Indian Chart of Account"
+msgstr "Plan comptable indian"
+
+#. module: l10n_in
+#: model:ir.actions.todo,note:l10n_in.config_call_account_template_in_minimal
+msgid ""
+"Generate Chart of Accounts from a Chart Template. You will be asked to pass "
+"the name of the company, the chart template to follow, the no. of digits to "
+"generate the code for your accounts and Bank account, currency to create "
+"Journals. Thus,the pure copy of chart Template is generated.\n"
+"\tThis is the same wizard that runs from Financial "
+"Management/Configuration/Financial Accounting/Financial Accounts/Generate "
+"Chart of Accounts from a Chart Template."
+msgstr ""
diff --git a/addons/l10n_th/i18n/th.po b/addons/l10n_th/i18n/th.po
new file mode 100644
index 00000000000..d9b675d399b
--- /dev/null
+++ b/addons/l10n_th/i18n/th.po
@@ -0,0 +1,43 @@
+# Thai 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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-07 06:19+0000\n"
+"PO-Revision-Date: 2011-11-17 01:32+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Thai \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-18 05:13+0000\n"
+"X-Generator: Launchpad (build 14299)\n"
+
+#. module: l10n_th
+#: model:ir.actions.todo,note:l10n_th.config_call_account_template_th
+msgid ""
+"Generate Chart of Accounts from a Chart Template. You will be asked to pass "
+"the name of the company, the chart template to follow, the no. of digits to "
+"generate the code for your accounts and Bank account, currency to create "
+"Journals. Thus,the pure copy of chart Template is generated.\n"
+"This is the same wizard that runs from Financial "
+"Management/Configuration/Financial Accounting/Financial Accounts/Generate "
+"Chart of Accounts from a Chart Template."
+msgstr ""
+
+#. module: l10n_th
+#: model:ir.module.module,shortdesc:l10n_th.module_meta_information
+msgid "Thailand - Thai Chart of Accounts"
+msgstr ""
+
+#. module: l10n_th
+#: model:ir.module.module,description:l10n_th.module_meta_information
+msgid ""
+"\n"
+"Chart of accounts for Thailand.\n"
+" "
+msgstr ""
diff --git a/addons/lunch/lunch_view.xml b/addons/lunch/lunch_view.xml
index 50932a03437..f695d994acd 100644
--- a/addons/lunch/lunch_view.xml
+++ b/addons/lunch/lunch_view.xml
@@ -371,9 +371,11 @@
-
+
+
report.lunch.amount
form
tree,form
+ {'search_default_year': 1,"search_default_month":1}
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index 9c535966e17..159259f151d 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -479,11 +479,22 @@ class mail_message(osv.osv):
attachments = []
for attach in message.attachment_ids:
attachments.append((attach.datas_fname, base64.b64decode(attach.datas)))
+
+ body = message.body_html if message.subtype == 'html' else message.body_text
+ body_alternative = None
+ subtype_alternative = None
+ if message.subtype == 'html' and message.body_text:
+ # we have a plain text alternative prepared, pass it to
+ # build_message instead of letting it build one
+ body_alternative = message.body_text
+ subtype_alternative = 'plain'
+
msg = ir_mail_server.build_email(
email_from=message.email_from,
email_to=to_email(message.email_to),
subject=message.subject,
- body=message.body_html if message.subtype == 'html' else message.body_text,
+ body=body,
+ body_alternative=body_alternative,
email_cc=to_email(message.email_cc),
email_bcc=to_email(message.email_bcc),
reply_to=message.reply_to,
@@ -491,6 +502,7 @@ class mail_message(osv.osv):
references = message.references,
object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)),
subtype=message.subtype,
+ subtype_alternative=subtype_alternative,
headers=message.headers and literal_eval(message.headers))
res = ir_mail_server.send_email(cr, uid, msg,
mail_server_id=message.mail_server_id.id,
diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml
index 83e7474fe1a..3169bca76c2 100644
--- a/addons/mail/mail_message_view.xml
+++ b/addons/mail/mail_message_view.xml
@@ -1,9 +1,6 @@
-
-
-
mail.message.form
mail.message
@@ -100,8 +97,10 @@
search
-
+
+
+
@@ -132,7 +131,7 @@
form
tree,form
[('email_from', '!=', False)]
- {'search_default_outgoing':1}
+ {'search_default_received':1}
@@ -142,14 +141,10 @@
src_model="res.partner"
view_id="view_email_message_tree"/>
-
-
-
-
diff --git a/addons/membership/i18n/pt.po b/addons/membership/i18n/pt.po
index 4e956c14e09..8d6d58052ec 100644
--- a/addons/membership/i18n/pt.po
+++ b/addons/membership/i18n/pt.po
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2011-01-11 11:15+0000\n"
-"PO-Revision-Date: 2010-12-12 07:51+0000\n"
-"Last-Translator: Madalena_prime \n"
+"PO-Revision-Date: 2011-11-23 12:23+0000\n"
+"Last-Translator: Stephane Wirtel (OpenERP) \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2011-11-05 04:59+0000\n"
-"X-Generator: Launchpad (build 14231)\n"
+"X-Launchpad-Export-Date: 2011-11-24 05:00+0000\n"
+"X-Generator: Launchpad (build 14336)\n"
#. module: membership
#: model:process.transition,name:membership.process_transition_invoicetoassociate0
@@ -474,7 +474,7 @@ msgstr "Versão da Lista de Preço de Venda do Membro"
msgid ""
"Error: The default UOM and the purchase UOM must be in the same category."
msgstr ""
-"Erro: O UOM por defeito e o UOM de compra devem estar na mesma categoria."
+"Erro: A UdM pré-defenida e a UdM de compra devem ser da mesma categoria."
#. module: membership
#: view:report.membership:0
diff --git a/addons/membership/membership_view.xml b/addons/membership/membership_view.xml
index b14a72b278e..a1c39165c82 100644
--- a/addons/membership/membership_view.xml
+++ b/addons/membership/membership_view.xml
@@ -107,7 +107,7 @@
Membership Products
product.product
[('membership','=',True), ('type', '=', 'service')]
- {'membership':True, 'type':'service'}
+ {'membership':True, 'type':'service', 'default_membership': True, 'default_type': 'service'}
diff --git a/addons/membership/report/report_membership_view.xml b/addons/membership/report/report_membership_view.xml
index 27ccfae0e12..b0ac01ac89f 100644
--- a/addons/membership/report/report_membership_view.xml
+++ b/addons/membership/report/report_membership_view.xml
@@ -36,9 +36,10 @@
-
+
+
@@ -96,7 +97,7 @@
report.membership
form
- {"search_default_member":1, 'search_default_Revenue':1, 'search_default_this_month':1, 'search_default_salesman':1,'group_by_no_leaf':1}
+ {"search_default_year":1,"search_default_member":1, 'search_default_Revenue':1, 'search_default_this_month':1, 'search_default_salesman':1,'group_by_no_leaf':1}
diff --git a/addons/membership/test/test_membership.yml b/addons/membership/test/test_membership.yml
index 75e0f887cfe..27b755f156b 100644
--- a/addons/membership/test/test_membership.yml
+++ b/addons/membership/test/test_membership.yml
@@ -13,36 +13,22 @@
list_price: 80.00
- |
- "Mark Johnson" want to join "Gold Membership".
-- |
- I'm creating new member "Mark Johnson".
+ "Seagate" want to join "Gold Membership", so I'm checking "Current Membership State" of "Seagate". It is an "Non Member" or not.
-
- !record {model: res.partner, id: res_partner_markjohnson0}:
- address:
- - city: paris
- country_id: base.fr
- name: Mark Johnson
- street: 1 rue Rockfeller
- type: invoice
- zip: '75016'
- name: Mark Johnson
-- |
- I'm checking "Current Membership State" of "Mark Johnson". It is an "Non Member" or not.
--
- !assert {model: res.partner, id: res_partner_markjohnson0}:
+ !assert {model: res.partner, id: base.res_partner_seagate}:
- membership_state == 'none', 'Member should be has "Current Membership State" in "Non Member".'
- |
- I'm doing to make membership invoice for "Mark Johnson" on joining "Gold Membership".
+ I'm doing to make membership invoice for "Seagate" on joining "Gold Membership".
-
!python {model: res.partner}: |
- self.create_membership_invoice(cr, uid, [ref("res_partner_markjohnson0")], product_id=ref("product_product_membershipproduct0"), datas={"amount":80.00})
+ self.create_membership_invoice(cr, uid, [ref("base.res_partner_seagate")], product_id=ref("product_product_membershipproduct0"), datas={"amount":80.00})
- |
- I'm checking "Current Membership State" of "Mark Johnson". It is an "Waiting Member" or not.
+ I'm checking "Current Membership State" of "Seagate". It is an "Waiting Member" or not.
-
- !assert {model: res.partner, id: res_partner_markjohnson0}:
+ !assert {model: res.partner, id: base.res_partner_seagate}:
- membership_state == 'waiting', 'Member should be has "Current Membership State" in "Waiting Member".'
- |
- I'm Opening that Invoice which is created for "Mark Johnson".
+ I'm Opening that Invoice which is created for "Seagate".
-
!python {model: res.partner}: |
import netsvc
@@ -52,16 +38,16 @@
membership_line_pool = self.pool.get('membership.membership_line')
membership_pool = self.pool.get('product.product')
- membership_line_ids = membership_line_pool.search(cr, uid, [('membership_id','=',ref('product_product_membershipproduct0')),('partner','=',ref('res_partner_markjohnson0'))])
+ membership_line_ids = membership_line_pool.search(cr, uid, [('membership_id','=',ref('product_product_membershipproduct0')),('partner','=',ref('base.res_partner_seagate'))])
membership_lines = membership_line_pool.browse(cr, uid, membership_line_ids)
assert membership_lines, _('Membership is not registrated.')
membership_line = membership_lines[0]
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.invoice', membership_line.account_invoice_id.id, 'invoice_open', cr)
- |
- I'm checking "Current membership state" of "Mark Johnson". It is an "Invoiced Member" or not.
+ I'm checking "Current membership state" of "Seagate". It is an "Invoiced Member" or not.
-
- !assert {model: res.partner, id: res_partner_markjohnson0}:
+ !assert {model: res.partner, id: base.res_partner_seagate}:
- membership_state == 'invoiced', 'Member should be has "Current Membership State" in "Invoiced Member".'
- |
@@ -85,10 +71,10 @@
- membership_state == 'free', 'Member should be has "Current Membership State" in "Free Member".'
- |
- I'm set "Mark Johnson" as a associated member of "Ms. Johnson" and also set Non free member.
+ I'm set "Seagate" as a associated member of "Ms. Johnson" and also set Non free member.
-
!python {model: res.partner}: |
- self.write(cr, uid, [ref("res_partner_msjohnson0")], {'free_member': False, 'associate_member': ref("res_partner_markjohnson0")})
+ self.write(cr, uid, [ref("res_partner_msjohnson0")], {'free_member': False, 'associate_member': ref("base.res_partner_seagate")})
- |
I'm checking "Current membership state" of "Ms. Johnson". It is an "Paid Member" or not.
@@ -108,17 +94,17 @@
type: service
list_price: 50.00
- |
- I'm making invoice of "Mark Johnson" member on joining new membership "Regular Membership".
+ I'm making invoice of "Seagate" member on joining new membership "Regular Membership".
-
!python {model: res.partner}: |
- self.create_membership_invoice(cr, uid, [ref("res_partner_markjohnson0")], product_id=ref("product_product_membershipproduct1"), datas={"amount":50.00})
+ self.create_membership_invoice(cr, uid, [ref("base.res_partner_seagate")], product_id=ref("product_product_membershipproduct1"), datas={"amount":50.00})
- |
- I'm checking "Current membership state" of "Mark Johnson". It is an "Old Member" or not.
+ I'm checking "Current membership state" of "Seagate". It is an "Old Member" or not.
-
- !assert {model: res.partner, id: res_partner_markjohnson0}:
+ !assert {model: res.partner, id: base.res_partner_seagate}:
- membership_state == 'old', 'Member should be has "Current Membership State" in "Old Member".'
- |
- I'm doing to make credit note of invoice which is paid by "Mark Johnson" to cancel membership.
+ I'm doing to make credit note of invoice which is paid by "Seagate" to cancel membership.
-
!python {model: account.invoice}: |
from tools.translate import _
@@ -128,15 +114,15 @@
membership_pool = self.pool.get('product.product')
invoice_refund_pool = self.pool.get('account.invoice.refund')
- membership_line_ids = membership_line_pool.search(cr, uid, [('membership_id','=',ref('product_product_membershipproduct0')),('partner','=',ref('res_partner_markjohnson0'))])
+ membership_line_ids = membership_line_pool.search(cr, uid, [('membership_id','=',ref('product_product_membershipproduct0')),('partner','=',ref('base.res_partner_seagate'))])
membership_lines = membership_line_pool.browse(cr, uid, membership_line_ids)
assert membership_lines, _('Membership is not registrated.')
membership_line = membership_lines[0]
refund_id = invoice_refund_pool.create(cr, uid, {'description': 'Refund of Membership', 'filter_refund': 'refund'}, {'active_id': membership_line.account_invoice_id.id})
invoice_refund_pool.invoice_refund(cr, uid, [refund_id], {'active_id': membership_line.account_invoice_id.id, 'active_ids': [membership_line.account_invoice_id.id]})
- |
- I'm checking "Current membership state" of "Mark Johnson". It is an "Cancelled Member" or not.
+ I'm checking "Current membership state" of "Seagate". It is an "Cancelled Member" or not.
-
- !assert {model: res.partner, id: res_partner_markjohnson0}:
+ !assert {model: res.partner, id: base.res_partner_seagate}:
- membership_state == 'canceled', 'Member should be has "Current Membership State" in "Cancelled Member".'
diff --git a/addons/mrp/board_manufacturing_view.xml b/addons/mrp/board_manufacturing_view.xml
index 03d6947d215..427780f344c 100644
--- a/addons/mrp/board_manufacturing_view.xml
+++ b/addons/mrp/board_manufacturing_view.xml
@@ -7,17 +7,16 @@
form
diff --git a/addons/mrp/i18n/pt.po b/addons/mrp/i18n/pt.po
index f67b9afca9c..9574e9dc573 100644
--- a/addons/mrp/i18n/pt.po
+++ b/addons/mrp/i18n/pt.po
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2011-01-11 11:15+0000\n"
-"PO-Revision-Date: 2011-11-09 17:38+0000\n"
-"Last-Translator: DReis \n"
+"PO-Revision-Date: 2011-11-23 11:53+0000\n"
+"Last-Translator: Daniel Reis \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2011-11-10 04:57+0000\n"
-"X-Generator: Launchpad (build 14263)\n"
+"X-Launchpad-Export-Date: 2011-11-24 05:00+0000\n"
+"X-Generator: Launchpad (build 14336)\n"
#. module: mrp
#: field:mrp.production,move_created_ids:0
@@ -1801,7 +1801,7 @@ msgstr "Empresa"
#. module: mrp
#: field:mrp.workcenter,time_cycle:0
msgid "Time for 1 cycle (hour)"
-msgstr "Tempo de um ciclo"
+msgstr "Tempo por ciclo (horas)"
#. module: mrp
#: model:ir.actions.report.xml,name:mrp.report_mrp_production_report
diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index 0fd75d7ca7a..356a762c0c5 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -491,6 +491,9 @@ class mrp_production(osv.osv):
'name': lambda x, y, z, c: x.pool.get('ir.sequence').get(y, z, 'mrp.production') or '/',
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.production', context=c),
}
+ _sql_constraints = [
+ ('name_uniq', 'unique(name, company_id)', 'Reference must be unique per Company!'),
+ ]
_order = 'priority desc, date_planned asc';
def _check_qty(self, cr, uid, ids, context=None):
diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml
index 13ac6287e52..67890b42134 100644
--- a/addons/mrp/mrp_view.xml
+++ b/addons/mrp/mrp_view.xml
@@ -529,7 +529,6 @@
Bill of Materials Structure
-
diff --git a/addons/mrp/test/mrp_phantom_bom.yml b/addons/mrp/test/mrp_phantom_bom.yml
index 41f1920dc2d..ca29b24ea23 100644
--- a/addons/mrp/test/mrp_phantom_bom.yml
+++ b/addons/mrp/test/mrp_phantom_bom.yml
@@ -179,11 +179,9 @@
-
!record {model: mrp.production, id: mrp_production_mo0}:
bom_id: mrp.mrp_bom_orangejuice0
- company_id: base.main_company
date_planned: !eval datetime.today().strftime("%Y-%m-%d %H:%M:%S")
location_dest_id: stock.stock_location_output
location_src_id: stock.stock_location_stock
- name: MO/00002
product_id: mrp.product_product_orangejuice0
product_lines:
- name: Orange
diff --git a/addons/mrp_operations/mrp_operations_view.xml b/addons/mrp_operations/mrp_operations_view.xml
index 2484345de9a..377a57d6121 100644
--- a/addons/mrp_operations/mrp_operations_view.xml
+++ b/addons/mrp_operations/mrp_operations_view.xml
@@ -268,8 +268,6 @@
Work Orders
-
-
).
+#
+# 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 .
+#
+##############################################################################
+
+import plugin_handler
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/plugin/__openerp__.py b/addons/plugin/__openerp__.py
new file mode 100644
index 00000000000..b598690cbf2
--- /dev/null
+++ b/addons/plugin/__openerp__.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+
+{
+ 'name': 'Common interface for CRM plugin (Thunderbird and Outlook)',
+ 'version': '1.0',
+ 'category': 'Hidden',
+ 'complexity': "easy",
+ 'description': """
+The common interface for pugin.
+=====================================================
+
+""",
+ 'author': 'OpenERP SA',
+ 'website': 'http://www.openerp.com',
+ 'depends': [
+ 'base',
+ ],
+ 'data': [],
+ 'demo': [],
+ 'test': [],
+ 'installable': True,
+ 'active': False,
+ 'images': [],
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py
new file mode 100644
index 00000000000..c4859771e35
--- /dev/null
+++ b/addons/plugin/plugin_handler.py
@@ -0,0 +1,153 @@
+'''
+Created on 18 oct. 2011
+
+@author: openerp
+'''
+
+from osv import osv, fields
+
+
+class plugin_handler(osv.osv_memory):
+ _name = 'plugin.handler'
+
+ def _make_url(self, cr, uid, res_id, model, context=None):
+ """
+ @param id: on which document the message is pushed
+ @param model: name of the document linked with the mail
+ @return url
+ """
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context)
+ if base_url:
+ base_url += '/?id=%s&model=%s'%(res_id,model)
+ return base_url
+
+ def is_installed(self, cr, uid):
+ return True
+
+ def partner_get(self, cr, uid, address_email):
+ ids = self.pool.get('res.partner.address').search(cr, uid, [('partner_id', '!=', False), ('email', 'like', address_email)])
+ res_id = ids and self.pool.get('res.partner.address').browse(cr, uid, ids[0]).partner_id.id or 0
+ url = self._make_url(cr, uid, res_id, 'res.partner')
+ return ('res.partner', res_id , url)
+
+ def document_get(self, cr, uid, email):
+ """
+ @param email: email is a standard RFC2822 email message
+ @return Dictionary which contain id and the model name of the document linked with the mail
+ if no document is found the id = 0
+ (model_name, res_id, name_get, url)
+ """
+ mail_message_obj = self.pool.get('mail.message')
+ model = False
+ res_id = 0
+ url = False
+ name = ""
+
+ msg = mail_message_obj.parse_message(email)
+ references = [msg.get('message-id')]
+ refs = msg.get('references',False)
+ if refs:
+ references.extend(refs.split())
+
+ msg_ids = mail_message_obj.search(cr, uid, [('message_id','in',references)])
+ if msg_ids:
+ msg = mail_message_obj.browse(cr, uid, msg_ids[0])
+ res_id = msg.res_id
+ model = msg.model
+ url = self._make_url(cr, uid, res_id, model)
+ name = self.pool.get(model).name_get(cr, uid, res_id)[1]
+ return (model, res_id, url, name)
+
+ def document_type(self, cr, uid, context=None):
+ """
+ Return the list of available model to push
+ res.partner is a special case
+ otherwise all model that inherit from mail.thread
+ ['res.partner', 'project.issue']
+ """
+ mail_thread_obj = self.pool.get('mail.thread')
+ doc_dict = mail_thread_obj.message_capable_models(cr, uid, context)
+ doc_dict['res.partner'] = "Partner"
+ return doc_dict.items()
+
+ # Can be used where search record was used
+ def list_document_get(self, cr, uid, model, name):
+ """
+ This function return the result of name_search on the object model
+ @param model: the name of the model
+ @param : the name of the document
+ @return : the result of name_search a list of tuple
+ [(id, 'name')]
+ """
+ return self.pool.get(model).name_search(cr,uid,name)
+
+ def push_message(self, cr, uid, model, email, res_id=0):
+ """
+ @param email: email is a standard RFC2822 email message
+ @param model: On which model the message is pushed
+ @param thread_id: on which document the message is pushed, if thread_id = 0 a new document is created
+ @return Dictionary which contain model , url and resource id.
+ """
+ mail_message = self.pool.get('mail.message')
+ model_obj = self.pool.get(model)
+
+ msg = mail_message.parse_message(email)
+ message_id = msg.get('message-id')
+
+
+ mail_ids = mail_message.search(cr, uid, [('message_id','=',message_id),('res_id','=',res_id),('model','=',model)])
+ if message_id and mail_ids :
+ mail_record = mail_message.browse(cr, uid, mail_ids)[0]
+ res_id = mail_record.res_id
+ notify = "Email already pushed"
+ elif res_id == 0:
+ if model == 'res.partner':
+ notify = 'User the button Partner to create a new partner'
+ else:
+ res_id = model_obj.message_new(cr, uid, msg)
+ notify = "Mail succefully pushed, a new %s has been created " % model
+ else:
+ model_obj.message_append_dict(cr, uid, [res_id], msg)
+ notify = "Mail succefully pushed"
+ url = self._make_url(cr, uid, res_id, model)
+ return (model, res_id, url, notify)
+
+ def contact_create(self, cr, uid, data, partner_id):
+ """
+ @param data : the data use to create the res.partner.address
+ [('field_name', value)], field name is required
+ @param partner_id : On which partner the address is attached
+ if partner_id = 0 then create a new partner with the same name that the address
+ @return : the partner_id sended or created, this allow the plugin to open the right partner page
+ """
+ partner_obj = self.pool.get('res.partner')
+ dictcreate = dict(data)
+ if partner_id == 0:
+ partner_id = partner_obj.create(cr, uid, {'name':dictcreate.get('name')})
+ dictcreate['partner_id'] = partner_id
+ self.pool.get('res.partner.address').create(cr, uid, dictcreate)
+ url = self._make_url(cr, uid, partner_id, 'res.partner')
+ return ('res.partner', partner_id, url)
+
+ # Specific to outlook rfc822 is not available so we split in arguments headerd,body,attachemnts
+ def push_message_outlook(self, cr, uid, model, headers, body_text, body_html, attachments):
+ pass
+ # ----------------------------------------
+ # solution 1
+ # construct a fake rfc822 from the separated arguement
+ #m = email.asdfsadf
+ # use the push_message method
+ #self.push_message(m)
+ # ----------------------------------------
+ # solution 2
+ # use self.pushmessage only with header and body
+ # add attachemnt yourself after
+
+ #ir_attachment_obj = self.pool.get('ir.attachment')
+ #attachment_ids = ir_attachment_obj.search(cr, uid, [('res_model', '=', data.get('res_model')), ('res_id', '=', data.get('res_id')), ('datas_fname', '=', data.get('datas_fname'))])
+ #if attachment_ids:
+ # return attachment_ids[0]
+ #else:
+ # vals = {"res_model": data.get('res_model'), "res_id": data.get('res_id'), "name": data.get('name'), "datas" : data.get('datas'), "datas_fname" : data.get('datas_fname')}
+ # return ir_attachment_obj.create(cr, uid, vals)
+ #return (model, res_id, url, notify)
diff --git a/addons/point_of_sale/static/src/js/pos.js b/addons/point_of_sale/static/src/js/pos.js
index 59c13e0553c..d1611cd87d8 100644
--- a/addons/point_of_sale/static/src/js/pos.js
+++ b/addons/point_of_sale/static/src/js/pos.js
@@ -162,11 +162,11 @@ openerp.point_of_sale = function(db) {
/* global variable */
var pos;
- var App, CashRegister, CashRegisterCollection, Category, CategoryCollection, CategoryView,
- NumpadState, NumpadView, Order, OrderButtonView, OrderCollection, OrderView, Orderline,
- OrderlineCollection, OrderlineView, PaymentButtonView, PaymentView, Paymentline,
- PaymentlineCollection, PaymentlineView, PaypadView, Product, ProductCollection,
- ProductListView, ProductView, ReceiptLineView, ReceiptView, Shop, ShopView, StepsView;
+ var App, CashRegister, CashRegisterCollection, Category, CategoryCollection, CategoryWidget,
+ NumpadState, NumpadWidget, Order, OrderButtonView, OrderCollection, OrderWidget, Orderline,
+ OrderlineCollection, OrderlineWidget, PaymentButtonWidget, PaymentWidget, Paymentline,
+ PaymentlineCollection, PaymentlineWidget, PaypadWidget, Product, ProductCollection,
+ ProductListWidget, ProductWidget, ReceiptLineWidget, ReceiptWidget, Shop, ShopView, StepsWidget;
/*
---
@@ -530,57 +530,48 @@ openerp.point_of_sale = function(db) {
Views
---
*/
- NumpadView = (function() {
- __extends(NumpadView, Backbone.View);
- function NumpadView() {
- NumpadView.__super__.constructor.apply(this, arguments);
- }
-
- NumpadView.prototype.initialize = function(options) {
- return this.state = options.state;
- };
- NumpadView.prototype.events = {
- 'click button#numpad-backspace': 'clickDeleteLastChar',
- 'click button#numpad-minus': 'clickSwitchSign',
- 'click button.number-char': 'clickAppendNewChar',
- 'click button.mode-button': 'clickChangeMode'
- };
- NumpadView.prototype.clickDeleteLastChar = function() {
+ NumpadWidget = db.web.Widget.extend({
+ init: function(parent, element_id, options) {
+ this._super(parent, element_id);
+ this.state = options.state;
+ },
+ start: function() {
+ this.$element.find('button#numpad-backspace').click(_.bind(this.clickDeleteLastChar, this));
+ this.$element.find('button#numpad-minus').click(_.bind(this.clickSwitchSign, this));
+ this.$element.find('button.number-char').click(_.bind(this.clickAppendNewChar, this));
+ this.$element.find('button.mode-button').click(_.bind(this.clickChangeMode, this));
+ },
+ clickDeleteLastChar: function() {
return this.state.deleteLastChar();
- };
- NumpadView.prototype.clickSwitchSign = function() {
+ },
+ clickSwitchSign: function() {
return this.state.switchSign();
- };
- NumpadView.prototype.clickAppendNewChar = function(event) {
+ },
+ clickAppendNewChar: function(event) {
var newChar;
newChar = event.currentTarget.innerText;
return this.state.appendNewChar(newChar);
- };
- NumpadView.prototype.clickChangeMode = function(event) {
+ },
+ clickChangeMode: function(event) {
var newMode;
$('.selected-mode').removeClass('selected-mode');
$(event.currentTarget).addClass('selected-mode');
newMode = event.currentTarget.attributes['data-mode'].nodeValue;
return this.state.changeMode(newMode);
- };
- return NumpadView;
- })();
+ }
+ });
/*
Gives access to the payment methods (aka. 'cash registers')
*/
- PaypadView = (function() {
- __extends(PaypadView, Backbone.View);
- function PaypadView() {
- PaypadView.__super__.constructor.apply(this, arguments);
- }
-
- PaypadView.prototype.initialize = function(options) {
- return this.shop = options.shop;
- };
- PaypadView.prototype.events = {
- 'click button': 'performPayment'
- };
- PaypadView.prototype.performPayment = function(event) {
+ PaypadWidget = db.web.Widget.extend({
+ init: function(parent, element_id, options) {
+ this._super(parent, element_id);
+ this.shop = options.shop;
+ },
+ start: function() {
+ this.$element.find('button').click(_.bind(this.performPayment, this));
+ },
+ performPayment: function(event) {
var cashRegister, cashRegisterCollection, cashRegisterId;
/* set correct view */
$('.step-screen').hide();
@@ -592,32 +583,26 @@ openerp.point_of_sale = function(db) {
return (item.get('id')) === parseInt(cashRegisterId, 10);
}, this));
return (this.shop.get('selectedOrder')).addPaymentLine(cashRegister);
- };
- PaypadView.prototype.render = function() {
- $(this.el).empty();
+ },
+ render_element: function() {
+ this.$element.empty();
return (this.shop.get('cashRegisters')).each(__bind( function(cashRegister) {
- return $(this.el).append((new PaymentButtonView({
- model: cashRegister
- })).render());
+ var button = new PaymentButtonWidget();
+ button.model = cashRegister;
+ button.appendTo(this.$element);
}, this));
- };
- return PaypadView;
- })();
- PaymentButtonView = (function() {
- __extends(PaymentButtonView, Backbone.View);
- function PaymentButtonView() {
- PaymentButtonView.__super__.constructor.apply(this, arguments);
}
-
- PaymentButtonView.prototype.template = qweb_template('pos-payment-button-template');
- PaymentButtonView.prototype.render = function() {
- return $(this.el).html(this.template({
+ });
+ PaymentButtonWidget = db.web.Widget.extend({
+ template_fct: qweb_template('pos-payment-button-template'),
+ render_element: function() {
+ this.$element.html(this.template_fct({
id: this.model.get('id'),
name: (this.model.get('journal_id'))[1]
}));
- };
- return PaymentButtonView;
- })();
+ return this;
+ }
+ });
/*
There are 3 steps in a POS workflow:
1. prepare the order (i.e. chose products, quantities etc.)
@@ -626,112 +611,101 @@ openerp.point_of_sale = function(db) {
It should be possible to go back to any step as long as step 3 hasn't been completed.
Modifying an order after validation shouldn't be allowed.
*/
- StepsView = (function() {
- __extends(StepsView, Backbone.View);
- function StepsView() {
- StepsView.__super__.constructor.apply(this, arguments);
- }
-
- StepsView.prototype.initialize = function(options) {
- return this.step = "products";
- };
- StepsView.prototype.events = {
- 'click input.step-button': 'clickChangeStep'
- };
- StepsView.prototype.clickChangeStep = function(event) {
+ StepsWidget = db.web.Widget.extend({
+ init: function(parent, element_id) {
+ this._super(parent, element_id);
+ this.step = "products";
+ },
+ start: function() {
+ this.$element.find('input.step-button').click(_.bind(this.clickChangeStep, this));
+ },
+ clickChangeStep: function(event) {
var newStep;
newStep = event.currentTarget.attributes['data-step'].nodeValue;
$('.step-screen').hide();
$('#' + newStep + '-screen').show();
return this.step = newStep;
- };
- return StepsView;
- })();
+ }
+ });
/*
Shopping carts.
*/
- OrderlineView = (function() {
- __extends(OrderlineView, Backbone.View);
- function OrderlineView() {
- OrderlineView.__super__.constructor.apply(this, arguments);
- }
-
- OrderlineView.prototype.tagName = 'tr';
- OrderlineView.prototype.template = qweb_template('pos-orderline-template');
- OrderlineView.prototype.initialize = function(options) {
+ OrderlineWidget = db.web.Widget.extend({
+ tagName: 'tr',
+ template_fct: qweb_template('pos-orderline-template'),
+ init: function(parent, element_id, options) {
+ this._super(parent, element_id);
+ this.model = options.model;
this.model.bind('change', __bind( function() {
- $(this.el).hide();
- return this.render();
+ this.$element.hide();
+ this.render_element();
}, this));
this.model.bind('remove', __bind( function() {
- return $(this.el).remove();
+ return this.$element.remove();
}, this));
this.order = options.order;
- return this.numpadState = options.numpadState;
- };
- OrderlineView.prototype.events = {
- 'click': 'clickHandler'
- };
- OrderlineView.prototype.clickHandler = function() {
+ this.numpadState = options.numpadState;
+ },
+ start: function() {
+ this.$element.click(_.bind(this.clickHandler, this));
+ },
+ clickHandler: function() {
this.numpadState.reset();
return this.select();
- };
- OrderlineView.prototype.render = function() {
+ },
+ render_element: function() {
this.select();
- return $(this.el).html(this.template(this.model.toJSON())).fadeIn(400, function() {
+ return this.$element.html(this.template_fct(this.model.toJSON())).fadeIn(400, function() {
return $('#current-order').scrollTop($(this).offset().top);
});
- };
- OrderlineView.prototype.select = function() {
+ },
+ select: function() {
$('tr.selected').removeClass('selected');
- $(this.el).addClass('selected');
+ this.$element.addClass('selected');
return this.order.selected = this.model;
- };
- return OrderlineView;
- })();
- OrderView = (function() {
- __extends(OrderView, Backbone.View);
- function OrderView() {
- OrderView.__super__.constructor.apply(this, arguments);
- }
-
- OrderView.prototype.initialize = function(options) {
+ },
+ });
+ OrderWidget = db.web.Widget.extend({
+ init: function(parent, element_id, options) {
+ this._super(parent, element_id);
this.shop = options.shop;
this.numpadState = options.numpadState;
this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this);
- return this.bindOrderLineEvents();
- };
- OrderView.prototype.changeSelectedOrder = function() {
+ this.bindOrderLineEvents();
+ },
+ changeSelectedOrder: function() {
this.currentOrderLines.unbind();
this.bindOrderLineEvents();
- return this.render();
- };
- OrderView.prototype.bindOrderLineEvents = function() {
+ return this.render_element();
+ },
+ bindOrderLineEvents: function() {
this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines');
this.currentOrderLines.bind('add', this.addLine, this);
- this.currentOrderLines.bind('change', this.render, this);
+ this.currentOrderLines.bind('change', this.render_element, this);
return this.currentOrderLines.bind('remove', this.render, this);
- };
- OrderView.prototype.addLine = function(newLine) {
- $(this.el).append((new OrderlineView({
+ },
+ addLine: function(newLine) {
+ var line = new OrderlineWidget(null, null, {
model: newLine,
order: this.shop.get('selectedOrder'),
numpadState: this.numpadState
- })).render());
+ });
+ line.appendTo(this.$element);
return this.updateSummary();
- };
- OrderView.prototype.render = function() {
- $(this.el).empty();
+ },
+ render_element: function() {
+ this.$element.empty();
this.currentOrderLines.each(__bind( function(orderLine) {
- return $(this.el).append((new OrderlineView({
+ var line = new OrderlineWidget(null, null, {
model: orderLine,
order: this.shop.get('selectedOrder'),
numpadState: this.numpadState
- })).render());
+ });
+ line.appendTo(this.$element);
}, this));
return this.updateSummary();
- };
- OrderView.prototype.updateSummary = function() {
+ },
+ updateSummary: function() {
var currentOrder, tax, total, totalTaxExcluded;
currentOrder = this.shop.get('selectedOrder');
total = currentOrder.getTotal();
@@ -740,31 +714,25 @@ openerp.point_of_sale = function(db) {
$('#subtotal').html(totalTaxExcluded.toFixed(2)).hide().fadeIn();
$('#tax').html(tax.toFixed(2)).hide().fadeIn();
return $('#total').html(total.toFixed(2)).hide().fadeIn();
- };
- return OrderView;
- })();
+ },
+ });
/*
"Products" step.
*/
- CategoryView = (function() {
- __extends(CategoryView, Backbone.View);
- function CategoryView() {
- CategoryView.__super__.constructor.apply(this, arguments);
- }
-
- CategoryView.prototype.events = {
- 'click .oe-pos-categories-list a': 'changeCategory'
- };
-
- CategoryView.prototype.template = qweb_template('pos-category-template');
- CategoryView.prototype.render = function(ancestors, children) {
+ CategoryWidget = db.web.Widget.extend({
+ start: function() {
+ this.$element.find(".oe-pos-categories-list a").click(_.bind(this.changeCategory, this));
+ },
+ template_fct: qweb_template('pos-category-template'),
+ render_element: function() {
+ var self = this;
var c;
- return $(this.el).html(this.template({
+ this.$element.html(this.template_fct({
breadcrumb: (function() {
var _i, _len, _results;
_results = [];
- for (_i = 0, _len = ancestors.length; _i < _len; _i++) {
- c = ancestors[_i];
+ for (_i = 0, _len = self.ancestors.length; _i < _len; _i++) {
+ c = self.ancestors[_i];
_results.push(pos.categories[c]);
}
return _results;
@@ -772,88 +740,77 @@ openerp.point_of_sale = function(db) {
categories: (function() {
var _i, _len, _results;
_results = [];
- for (_i = 0, _len = children.length; _i < _len; _i++) {
- c = children[_i];
+ for (_i = 0, _len = self.children.length; _i < _len; _i++) {
+ c = self.children[_i];
_results.push(pos.categories[c]);
}
return _results;
})()
}));
- };
- CategoryView.prototype.changeCategory = function(a) {
+ },
+ changeCategory: function(a) {
var id = $(a.target).data("category-id");
- this.trigger("changeCategory", id);
- };
- return CategoryView;
- })();
- ProductView = (function() {
- __extends(ProductView, Backbone.View);
- function ProductView() {
- ProductView.__super__.constructor.apply(this, arguments);
- }
-
- ProductView.prototype.tagName = 'li';
- ProductView.prototype.className = 'product';
- ProductView.prototype.template = qweb_template('pos-product-template');
- ProductView.prototype.events = {
- 'click a': 'addToOrder'
- };
- ProductView.prototype.initialize = function(options) {
- return this.shop = options.shop;
- };
- ProductView.prototype.addToOrder = function(event) {
+ this.on_change_category(id);
+ },
+ on_change_category: function(id) {},
+ });
+ ProductWidget = db.web.Widget.extend({
+ tag_name:'li',
+ template_fct: qweb_template('pos-product-template'),
+ init: function(parent, element_id, options) {
+ this._super(parent, element_id);
+ this.model = options.model;
+ this.shop = options.shop;
+ },
+ start: function(options) {
+ $("a", this.$element).click(_.bind(this.addToOrder, this));
+ },
+ addToOrder: function(event) {
/* Preserve the category URL */
event.preventDefault();
return (this.shop.get('selectedOrder')).addProduct(this.model);
- };
- ProductView.prototype.render = function() {
- return $(this.el).html(this.template(this.model.toJSON()));
- };
- return ProductView;
- })();
- ProductListView = (function() {
- __extends(ProductListView, Backbone.View);
- function ProductListView() {
- ProductListView.__super__.constructor.apply(this, arguments);
- }
-
- ProductListView.prototype.tagName = 'ol';
- ProductListView.prototype.className = 'product-list';
- ProductListView.prototype.initialize = function(options) {
+ },
+ render_element: function() {
+ this.$element.addClass("product");
+ this.$element.html(this.template_fct(this.model.toJSON()));
+ return this;
+ },
+ });
+ ProductListWidget = db.web.Widget.extend({
+ init: function(parent, element_id, options) {
+ this._super(parent, element_id);
+ this.model = options.model;
this.shop = options.shop;
- return (this.shop.get('products')).bind('reset', this.render, this);
- };
- ProductListView.prototype.render = function() {
- $(this.el).empty();
+ this.shop.get('products').bind('reset', this.render_element, this);
+ },
+ render_element: function() {
+ this.$element.empty();
(this.shop.get('products')).each(__bind( function(product) {
- return $(this.el).append((new ProductView({
+ var p = new ProductWidget(null, null, {
model: product,
shop: this.shop
- })).render());
+ });
+ p.appendTo(this.$element);
}, this));
- return $('#products-screen').append(this.el);
- };
- return ProductListView;
- })();
+ return this;
+ },
+ });
/*
"Payment" step.
*/
- PaymentlineView = (function() {
- __extends(PaymentlineView, Backbone.View);
- function PaymentlineView() {
- PaymentlineView.__super__.constructor.apply(this, arguments);
- }
-
- PaymentlineView.prototype.tagName = 'tr';
- PaymentlineView.prototype.className = 'paymentline';
- PaymentlineView.prototype.template = qweb_template('pos-paymentline-template');
- PaymentlineView.prototype.initialize = function() {
- return this.model.bind('change', this.render, this);
- };
- PaymentlineView.prototype.events = {
- 'keyup input': 'changeAmount'
- };
- PaymentlineView.prototype.changeAmount = function(event) {
+ PaymentlineWidget = db.web.Widget.extend({
+ tag_name: 'tr',
+ template_fct: qweb_template('pos-paymentline-template'),
+ init: function(parent, element_id, options) {
+ this._super(parent, element_id);
+ this.model = options.model;
+ this.model.bind('change', this.render_element, this);
+ },
+ start: function () {
+ this.$element.addClass('paymentline');
+ $('input', this.$element).keyup(_.bind(this.changeAmount, this));
+ },
+ changeAmount: function(event) {
var newAmount;
newAmount = event.currentTarget.value;
if (newAmount && !isNaN(newAmount)) {
@@ -861,164 +818,155 @@ openerp.point_of_sale = function(db) {
amount: parseFloat(newAmount)
});
}
- };
- PaymentlineView.prototype.render = function() {
- return $(this.el).html(this.template({
+ },
+ render_element: function() {
+ this.$element.html(this.template_fct({
name: (this.model.get('journal_id'))[1],
amount: this.model.get('amount')
}));
- };
- return PaymentlineView;
- })();
- PaymentView = (function() {
- __extends(PaymentView, Backbone.View);
- function PaymentView() {
- PaymentView.__super__.constructor.apply(this, arguments);
- }
-
- PaymentView.prototype.initialize = function(options) {
+ return this;
+ },
+ });
+ PaymentWidget = db.web.Widget.extend({
+ init: function(parent, element_id, options) {
+ this._super(parent, element_id);
+ this.model = options.model;
this.shop = options.shop;
this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this);
this.bindPaymentLineEvents();
- return this.bindOrderLineEvents();
- };
- PaymentView.prototype.paymentLineList = function() {
- return $(this.el).find('#paymentlines');
- };
- PaymentView.prototype.events = {
- 'click button#validate-order': 'validateCurrentOrder'
- };
- PaymentView.prototype.validateCurrentOrder = function() {
+ this.bindOrderLineEvents();
+ },
+ paymentLineList: function() {
+ return this.$element.find('#paymentlines');
+ },
+ start: function() {
+ $('button#validate-order', this.$element).click(_.bind(this.validateCurrentOrder, this));
+ },
+ validateCurrentOrder: function() {
var callback, currentOrder;
currentOrder = this.shop.get('selectedOrder');
- callback = __bind( function() {
+ callback = __bind(function() {
return currentOrder.set({
validated: true
});
}, this);
- return pos.push('pos.order', currentOrder.exportAsJSON(), callback);
- };
- PaymentView.prototype.bindPaymentLineEvents = function() {
+ pos.push('pos.order', currentOrder.exportAsJSON(), callback);
+ },
+ bindPaymentLineEvents: function() {
this.currentPaymentLines = (this.shop.get('selectedOrder')).get('paymentLines');
this.currentPaymentLines.bind('add', this.addPaymentLine, this);
- this.currentPaymentLines.bind('change', this.render, this);
- this.currentPaymentLines.bind('remove', this.render, this);
- return this.currentPaymentLines.bind('all', this.updatePaymentSummary, this);
- };
- PaymentView.prototype.bindOrderLineEvents = function() {
+ this.currentPaymentLines.bind('all', this.updatePaymentSummary, this);
+ },
+ bindOrderLineEvents: function() {
this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines');
- return this.currentOrderLines.bind('all', this.updatePaymentSummary, this);
- };
- PaymentView.prototype.changeSelectedOrder = function() {
+ this.currentOrderLines.bind('all', this.updatePaymentSummary, this);
+ },
+ changeSelectedOrder: function() {
this.currentPaymentLines.unbind();
this.bindPaymentLineEvents();
this.currentOrderLines.unbind();
this.bindOrderLineEvents();
- return this.render();
- };
- PaymentView.prototype.addPaymentLine = function(newPaymentLine) {
- return this.paymentLineList().append((new PaymentlineView({
+ this.render_element();
+ },
+ addPaymentLine: function(newPaymentLine) {
+ var x = new PaymentlineWidget(null, null, {
model: newPaymentLine
- })).render());
- };
- PaymentView.prototype.render = function() {
+ });
+ x.appendTo(this.paymentLineList());
+ },
+ render_element: function() {
this.paymentLineList().empty();
this.currentPaymentLines.each(__bind( function(paymentLine) {
- return this.paymentLineList().append((new PaymentlineView({
- model: paymentLine
- })).render());
+ var x = new PaymentlineWidget(null, null, {
+ model: paymentLine
+ });
+ this.paymentLineList().append(x);
}, this));
- return this.updatePaymentSummary();
- };
- PaymentView.prototype.updatePaymentSummary = function() {
+ this.updatePaymentSummary();
+ },
+ updatePaymentSummary: function() {
var currentOrder, dueTotal, paidTotal, remaining, remainingAmount;
currentOrder = this.shop.get('selectedOrder');
paidTotal = currentOrder.getPaidTotal();
dueTotal = currentOrder.getTotal();
- $(this.el).find('#payment-due-total').html(dueTotal.toFixed(2));
- $(this.el).find('#payment-paid-total').html(paidTotal.toFixed(2));
+ this.$element.find('#payment-due-total').html(dueTotal.toFixed(2));
+ this.$element.find('#payment-paid-total').html(paidTotal.toFixed(2));
remainingAmount = dueTotal - paidTotal;
remaining = remainingAmount > 0 ? 0 : (-remainingAmount).toFixed(2);
- return $('#payment-remaining').html(remaining);
- };
- return PaymentView;
- })();
+ $('#payment-remaining').html(remaining);
+ },
+ });
/*
"Receipt" step.
*/
- ReceiptLineView = (function() {
- __extends(ReceiptLineView, Backbone.View);
- function ReceiptLineView() {
- ReceiptLineView.__super__.constructor.apply(this, arguments);
- }
-
- ReceiptLineView.prototype.tagName = 'tr';
- ReceiptLineView.prototype.className = 'receiptline';
- ReceiptLineView.prototype.template = qweb_template('pos-receiptline-template');
- ReceiptLineView.prototype.initialize = function() {
- return this.model.bind('change', this.render, this);
- };
- ReceiptLineView.prototype.render = function() {
- return $(this.el).html(this.template(this.model.toJSON()));
- };
- return ReceiptLineView;
- })();
- ReceiptView = (function() {
- __extends(ReceiptView, Backbone.View);
- function ReceiptView() {
- ReceiptView.__super__.constructor.apply(this, arguments);
- }
-
- ReceiptView.prototype.initialize = function(options) {
+ ReceiptLineWidget = db.web.Widget.extend({
+ tag_name: 'tr',
+ template_fct: qweb_template('pos-receiptline-template'),
+ init: function(parent, options) {
+ this._super(parent);
+ this.model = options.model;
+ this.model.bind('change', this.render_element, this);
+ },
+ render_element: function() {
+ this.$element.addClass('receiptline');
+ this.$element.html(this.template_fct(this.model.toJSON()));
+ },
+ });
+ ReceiptWidget = db.web.Widget.extend({
+ init: function(parent, options) {
+ this._super(parent);
+ this.model = options.model;
this.shop = options.shop;
this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this);
this.bindOrderLineEvents();
- return this.bindPaymentLineEvents();
- };
- ReceiptView.prototype.events = {
- "click button#pos-finish-order": "finishOrder"
- };
- ReceiptView.prototype.finishOrder = function() {
+ this.bindPaymentLineEvents();
+ },
+ start: function () {
+ $('button#pos-finish-order', this.$element).click(_.bind(this.finishOrder, this));
+ },
+ finishOrder: function() {
$('.step-screen').hide();
$('#products-screen').show();
this.shop.get('selectedOrder').destroy();
- };
- ReceiptView.prototype.receiptLineList = function() {
- return $(this.el).find('#receiptlines');
- };
- ReceiptView.prototype.bindOrderLineEvents = function() {
+ },
+ receiptLineList: function() {
+ return this.$element.find('#receiptlines');
+ },
+ bindOrderLineEvents: function() {
this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines');
this.currentOrderLines.bind('add', this.addReceiptLine, this);
- this.currentOrderLines.bind('change', this.render, this);
- return this.currentOrderLines.bind('remove', this.render, this);
- };
- ReceiptView.prototype.bindPaymentLineEvents = function() {
+ this.currentOrderLines.bind('change', this.render_element, this);
+ this.currentOrderLines.bind('remove', this.render_element, this);
+ },
+ bindPaymentLineEvents: function() {
this.currentPaymentLines = (this.shop.get('selectedOrder')).get('paymentLines');
- return this.currentPaymentLines.bind('all', this.updateReceiptSummary, this);
- };
- ReceiptView.prototype.changeSelectedOrder = function() {
+ this.currentPaymentLines.bind('all', this.updateReceiptSummary, this);
+ },
+ changeSelectedOrder: function() {
this.currentOrderLines.unbind();
this.bindOrderLineEvents();
this.currentPaymentLines.unbind();
this.bindPaymentLineEvents();
- return this.render();
- };
- ReceiptView.prototype.addReceiptLine = function(newOrderItem) {
- this.receiptLineList().append((new ReceiptLineView({
+ this.render_element();
+ },
+ addReceiptLine: function(newOrderItem) {
+ var x = new ReceiptLineWidget(null, {
model: newOrderItem
- })).render());
- return this.updateReceiptSummary();
- };
- ReceiptView.prototype.render = function() {
- this.receiptLineList().empty();
+ });
+ x.appendTo(this.receiptLineList());
+ this.updateReceiptSummary();
+ },
+ render_element: function() {
+ this.$element.html(qweb_template('pos-receipt-view'));
this.currentOrderLines.each(__bind( function(orderItem) {
- return this.receiptLineList().append((new ReceiptLineView({
+ var x = new ReceiptLineWidget(null, {
model: orderItem
- })).render());
+ });
+ x.appendTo(this.receiptLineList());
}, this));
- return this.updateReceiptSummary();
- };
- ReceiptView.prototype.updateReceiptSummary = function() {
+ this.updateReceiptSummary();
+ },
+ updateReceiptSummary: function() {
var change, currentOrder, tax, total;
currentOrder = this.shop.get('selectedOrder');
total = currentOrder.getTotal();
@@ -1026,10 +974,9 @@ openerp.point_of_sale = function(db) {
change = currentOrder.getPaidTotal() - total;
$('#receipt-summary-tax').html(tax.toFixed(2));
$('#receipt-summary-total').html(total.toFixed(2));
- return $('#receipt-summary-change').html(change.toFixed(2));
- };
- return ReceiptView;
- })();
+ $('#receipt-summary-change').html(change.toFixed(2));
+ },
+ });
OrderButtonView = (function() {
__extends(OrderButtonView, Backbone.View);
function OrderButtonView() {
@@ -1087,38 +1034,45 @@ openerp.point_of_sale = function(db) {
this.numpadState = new NumpadState({
shop: this.shop
});
- this.productListView = new ProductListView({
+ this.productListView = new ProductListWidget(null, "products-screen-ol", {
shop: this.shop
});
- this.paypadView = new PaypadView({
+ this.productListView.render_element();
+ this.productListView.start();
+ this.paypadView = new PaypadWidget(null, 'paypad', {
+ shop: this.shop
+ });
+ this.paypadView.render_element();
+ this.paypadView.start();
+ this.orderView = new OrderWidget(null, 'current-order-content', {
shop: this.shop,
- el: $('#paypad')
+ numpadState: this.numpadState
});
- this.paypadView.render();
- this.orderView = new OrderView({
+ this.orderView.start();
+ this.paymentView = new PaymentWidget(null, 'payment-screen', {
+ shop: this.shop
+ });
+ this.paymentView.render_element();
+ this.paymentView.start();
+ this.receiptView = new ReceiptWidget(null, {
shop: this.shop,
- numpadState: this.numpadState,
- el: $('#current-order-content')
});
- this.paymentView = new PaymentView({
- shop: this.shop,
- el: $('#payment-screen')
- });
- this.receiptView = new ReceiptView({
- shop: this.shop,
- el: $('#receipt-screen')
- });
- this.numpadView = new NumpadView({
- state: this.numpadState,
- el: $('#numpad')
- });
- return this.stepsView = new StepsView({
- el: $('#steps')
+ debugger;
+ this.receiptView.replace($('#receipt-screen'));
+ this.numpadView = new NumpadWidget(null, 'numpad', {
+ state: this.numpadState
});
+ this.numpadView.start();
+ this.stepsView = new StepsWidget(null, 'steps');
+ this.stepsView.start();
+ this.start();
};
ShopView.prototype.events = {
'click button#neworder-button': 'createNewOrder'
};
+ ShopView.prototype.start = function() {
+ this.productListView.start();
+ };
ShopView.prototype.createNewOrder = function() {
var newOrder;
newOrder = new Order;
@@ -1149,10 +1103,9 @@ openerp.point_of_sale = function(db) {
shop: this.shop,
el: $element
});
- this.categoryView = new CategoryView;
- this.categoryView.bind("changeCategory", this.category, this);
+ this.categoryView = new CategoryWidget(null, 'products-screen-categories');
+ this.categoryView.on_change_category.add_last(_.bind(this.category, this));
this.category();
- return this.categoryView;
};
App.prototype.category = function(id) {
var c, products;
@@ -1160,8 +1113,10 @@ openerp.point_of_sale = function(db) {
id = 0;
}
c = pos.categories[id];
- $('#products-screen').html(this.categoryView.render(c.ancestors, c.children));
- this.categoryView.delegateEvents();
+ this.categoryView.ancestors = c.ancestors;
+ this.categoryView.children = c.children;
+ this.categoryView.render_element();
+ this.categoryView.start();
products = pos.store.get('product.product').filter( function(p) {
var _ref;
return _ref = p.pos_categ_id[0], __indexOf.call(c.subtree, _ref) >= 0;
diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml
index b9148395cbe..74ff2229729 100644
--- a/addons/point_of_sale/static/src/xml/pos.xml
+++ b/addons/point_of_sale/static/src/xml/pos.xml
@@ -88,7 +88,10 @@
-
-
-
-
-
- OpenERP Point of Sale
-
-
-
-
-
-
- Total: €
- Tax: €
- Change: €
-
-
-
-
Next Order
-
-
+
@@ -237,4 +219,28 @@
Order
X
+
+
+
+
+
+
+ OpenERP Point of Sale
+
+
+
+
+
+
+ Total: €
+ Tax: €
+ Change: €
+
+
+
+
Next Order
+
+
+
\ No newline at end of file
diff --git a/addons/point_of_sale/test/01_order_to_payment.yml b/addons/point_of_sale/test/01_order_to_payment.yml
index fd8ffbaa3e0..7c6b4817666 100644
--- a/addons/point_of_sale/test/01_order_to_payment.yml
+++ b/addons/point_of_sale/test/01_order_to_payment.yml
@@ -74,7 +74,7 @@
-
I click on the "Make Payment" wizard to pay the PoS order with a partial amount of 100.0 EUR
-
- !record {model: pos.make.payment, id: pos_make_payment_0, context: {'active_id': ref('pos_order_pos0'), 'active_ids': [ref('pos_order_pos0')]} }:
+ !record {model: pos.make.payment, id: pos_make_payment_0, context: '{"active_id": ref("pos_order_pos0"), "active_ids": [ref("pos_order_pos0")]}' }:
amount: 100.0
-
I click on the validate button to register the payment.
@@ -95,7 +95,7 @@
-
I pay the remaining balance.
-
- !record {model: pos.make.payment, id: pos_make_payment_1, context: {'active_id': [ref('pos_order_pos0')], 'active_ids': [ref('pos_order_pos0')]} }:
+ !record {model: pos.make.payment, id: pos_make_payment_1, context: '{"active_id": ref("pos_order_pos0"), "active_ids": [ref("pos_order_pos0")]}' }:
amount: !eval >
(450*2 + 300*3*1.05)*0.95-100.0
-
diff --git a/addons/point_of_sale/test/02_order_to_invoice.yml b/addons/point_of_sale/test/02_order_to_invoice.yml
index 3436e176678..0de4e518ebd 100644
--- a/addons/point_of_sale/test/02_order_to_invoice.yml
+++ b/addons/point_of_sale/test/02_order_to_invoice.yml
@@ -18,7 +18,7 @@
-
I click on the "Make Payment" wizard to pay the PoS order
-
- !record {model: pos.make.payment, id: pos_make_payment_2, context: {'active_id': ref('pos_order_pos1'), 'active_ids': [ref('pos_order_pos1')]} }:
+ !record {model: pos.make.payment, id: pos_make_payment_2, context: '{"active_id": ref("pos_order_pos1"), "active_ids": [ref("pos_order_pos1")]}' }:
amount: !eval >
(450*2 + 300*3*1.05)*0.95
-
diff --git a/addons/procurement/board_mrp_procurement_view.xml b/addons/procurement/board_mrp_procurement_view.xml
index bf11fe79166..b45edd3cbdd 100644
--- a/addons/procurement/board_mrp_procurement_view.xml
+++ b/addons/procurement/board_mrp_procurement_view.xml
@@ -16,8 +16,8 @@
form
-
-
+
+
diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py
index 3c22b2838ef..db953c8a319 100644
--- a/addons/product/pricelist.py
+++ b/addons/product/pricelist.py
@@ -202,6 +202,13 @@ class product_pricelist(osv.osv):
else:
categ_where = '(categ_id IS NULL)'
+ if partner:
+ partner_where = 'base <> -2 OR %s IN (SELECT name FROM product_supplierinfo WHERE product_id = %s) '
+ partner_args = (partner, product_id)
+ else:
+ partner_where = 'base <> -2 '
+ partner_args = ()
+
cr.execute(
'SELECT i.*, pl.currency_id '
'FROM product_pricelist_item AS i, '
@@ -209,11 +216,12 @@ class product_pricelist(osv.osv):
'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = %s) '
'AND (product_id IS NULL OR product_id = %s) '
'AND (' + categ_where + ' OR (categ_id IS NULL)) '
+ 'AND (' + partner_where + ') '
'AND price_version_id = %s '
'AND (min_quantity IS NULL OR min_quantity <= %s) '
'AND i.price_version_id = v.id AND v.pricelist_id = pl.id '
'ORDER BY sequence',
- (tmpl_id, product_id, pricelist_version_ids[0], qty))
+ (tmpl_id, product_id) + partner_args + (pricelist_version_ids[0], qty))
res1 = cr.dictfetchall()
uom_price_already_computed = False
for res in res1:
@@ -296,148 +304,6 @@ class product_pricelist(osv.osv):
res.update({'item_id': {ids[-1]: res_multi.get('item_id', ids[-1])}})
return res
- def price_get_old(self, cr, uid, ids, prod_id, qty, partner=None, context=None):
- '''
- context = {
- 'uom': Unit of Measure (int),
- 'partner': Partner ID (int),
- 'date': Date of the pricelist (%Y-%m-%d),
- }
- '''
- price = False
- item_id = 0
- if context is None:
- context = {}
- currency_obj = self.pool.get('res.currency')
- product_obj = self.pool.get('product.product')
- supplierinfo_obj = self.pool.get('product.supplierinfo')
- price_type_obj = self.pool.get('product.price.type')
-
- if context and ('partner_id' in context):
- partner = context['partner_id']
- context['partner_id'] = partner
- date = time.strftime('%Y-%m-%d')
- if context and ('date' in context):
- date = context['date']
- result = {}
- result['item_id'] = {}
- for id in ids:
- cr.execute('SELECT * ' \
- 'FROM product_pricelist_version ' \
- 'WHERE pricelist_id = %s AND active=True ' \
- 'AND (date_start IS NULL OR date_start <= %s) ' \
- 'AND (date_end IS NULL OR date_end >= %s) ' \
- 'ORDER BY id LIMIT 1', (id, date, date))
- plversion = cr.dictfetchone()
-
- if not plversion:
- raise osv.except_osv(_('Warning !'),
- _('No active version for the selected pricelist !\n' \
- 'Please create or activate one.'))
-
- cr.execute('SELECT id, categ_id ' \
- 'FROM product_template ' \
- 'WHERE id = (SELECT product_tmpl_id ' \
- 'FROM product_product ' \
- 'WHERE id = %s)', (prod_id,))
- tmpl_id, categ = cr.fetchone()
- categ_ids = []
- while categ:
- categ_ids.append(str(categ))
- cr.execute('SELECT parent_id ' \
- 'FROM product_category ' \
- 'WHERE id = %s', (categ,))
- categ = cr.fetchone()[0]
- if str(categ) in categ_ids:
- raise osv.except_osv(_('Warning !'),
- _('Could not resolve product category, ' \
- 'you have defined cyclic categories ' \
- 'of products!'))
- if categ_ids:
- categ_where = '(categ_id IN (' + ','.join(categ_ids) + '))'
- else:
- categ_where = '(categ_id IS NULL)'
-
- cr.execute(
- 'SELECT i.*, pl.currency_id '
- 'FROM product_pricelist_item AS i, '
- 'product_pricelist_version AS v, product_pricelist AS pl '
- 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = %s) '
- 'AND (product_id IS NULL OR product_id = %s) '
- 'AND (' + categ_where + ' OR (categ_id IS NULL)) '
- 'AND price_version_id = %s '
- 'AND (min_quantity IS NULL OR min_quantity <= %s) '
- 'AND i.price_version_id = v.id AND v.pricelist_id = pl.id '
- 'ORDER BY sequence',
- (tmpl_id, prod_id, plversion['id'], qty))
- res1 = cr.dictfetchall()
-
- for res in res1:
- item_id = 0
- if res:
- if res['base'] == -1:
- if not res['base_pricelist_id']:
- price = 0.0
- else:
- price_tmp = self.price_get(cr, uid,
- [res['base_pricelist_id']], prod_id,
- qty, context=context)[res['base_pricelist_id']]
- ptype_src = self.browse(cr, uid,
- res['base_pricelist_id']).currency_id.id
- price = currency_obj.compute(cr, uid, ptype_src,
- res['currency_id'], price_tmp, round=False)
- break
- elif res['base'] == -2:
- where = []
- if partner:
- where = [('name', '=', partner) ]
- sinfo = supplierinfo_obj.search(cr, uid,
- [('product_id', '=', tmpl_id)] + where)
- price = 0.0
- if sinfo:
- cr.execute('SELECT * ' \
- 'FROM pricelist_partnerinfo ' \
- 'WHERE suppinfo_id IN %s' \
- 'AND min_quantity <= %s ' \
- 'ORDER BY min_quantity DESC LIMIT 1', (tuple(sinfo),qty,))
- res2 = cr.dictfetchone()
- if res2:
- price = res2['price']
- break
- else:
- price_type = price_type_obj.browse(cr, uid, int(res['base']))
- price = currency_obj.compute(cr, uid,
- price_type.currency_id.id, res['currency_id'],
- product_obj.price_get(cr, uid, [prod_id],
- price_type.field, context=context)[prod_id], round=False, context=context)
-
- if price:
- price_limit = price
-
- price = price * (1.0+(res['price_discount'] or 0.0))
- price = rounding(price, res['price_round'])
- price += (res['price_surcharge'] or 0.0)
- if res['price_min_margin']:
- price = max(price, price_limit+res['price_min_margin'])
- if res['price_max_margin']:
- price = min(price, price_limit+res['price_max_margin'])
- item_id = res['id']
- break
-
- else:
- # False means no valid line found ! But we may not raise an
- # exception here because it breaks the search
- price = False
- result[id] = price
- result['item_id'] = {id: item_id}
- if context and ('uom' in context):
- product = product_obj.browse(cr, uid, prod_id)
- uom = product.uos_id or product.uom_id
- result[id] = self.pool.get('product.uom')._compute_price(cr,
- uid, uom.id, result[id], context['uom'])
-
- return result
-
product_pricelist()
diff --git a/addons/product/product.py b/addons/product/product.py
index 882a77e6f12..ecf1761318e 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -334,7 +334,7 @@ class product_template(osv.osv):
def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id):
if uom_id:
return {'value': {'uom_po_id': uom_id}}
- return False
+ return {}
def write(self, cr, uid, ids, vals, context=None):
if 'uom_po_id' in vals:
diff --git a/addons/product/product_data.xml b/addons/product/product_data.xml
index fc085aab922..122c8594de4 100644
--- a/addons/product/product_data.xml
+++ b/addons/product/product_data.xml
@@ -23,8 +23,8 @@
+ Resource: product.uom
+ -->
PCE
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index a0669795ad5..ac9ae8c5ec3 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -376,7 +376,6 @@
Products by Categories
-
diff --git a/addons/product/test/product_test.yml b/addons/product/test/product_test.yml
index f19b6a4a63c..ae75a552c79 100644
--- a/addons/product/test/product_test.yml
+++ b/addons/product/test/product_test.yml
@@ -5,7 +5,6 @@
name: 20KG
uom_type: bigger
category_id: product.product_uom_categ_kgm
- rounding: 0.010
factor_inv: 20
-
I create a 10KG UOM for 'Sugar'
@@ -14,14 +13,12 @@
name: 10KG
uom_type: bigger
category_id: product.product_uom_categ_kgm
- rounding: 0.010
factor_inv: 10
-
I create a new product 'Sugar' in 20KG UOM.
-
!record {model: product.product, id: product_sugar_id1}:
categ_id: 'product.product_category_rawmaterial0'
- cost_method: standard
name: Sugar 20KG
procure_method: make_to_order
standard_price: 400.0
diff --git a/addons/project/board_project_view.xml b/addons/project/board_project_view.xml
index f2c44fab574..a58557c94ba 100644
--- a/addons/project/board_project_view.xml
+++ b/addons/project/board_project_view.xml
@@ -81,16 +81,16 @@
form
diff --git a/addons/project/project.py b/addons/project/project.py
index 1fc83771005..abebd2d35ff 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -75,11 +75,14 @@ class project(osv.osv):
def onchange_partner_id(self, cr, uid, ids, part=False, context=None):
partner_obj = self.pool.get('res.partner')
if not part:
- return {'value':{'contact_id': False, 'pricelist_id': False}}
+ return {'value':{'contact_id': False}}
addr = partner_obj.address_get(cr, uid, [part], ['contact'])
- pricelist = partner_obj.read(cr, uid, part, ['property_product_pricelist'], context=context)
- pricelist_id = pricelist.get('property_product_pricelist', False) and pricelist.get('property_product_pricelist')[0] or False
- return {'value':{'contact_id': addr['contact'], 'pricelist_id': pricelist_id}}
+ val = {'contact_id': addr['contact']}
+ if 'pricelist_id' in self.fields_get(cr, uid, context=context):
+ pricelist = partner_obj.read(cr, uid, part, ['property_product_pricelist'], context=context)
+ pricelist_id = pricelist.get('property_product_pricelist', False) and pricelist.get('property_product_pricelist')[0] or False
+ val['pricelist_id'] = pricelist_id
+ return {'value': val}
def _progress_rate(self, cr, uid, ids, names, arg, context=None):
res = {}.fromkeys(ids, 0.0)
@@ -421,21 +424,55 @@ class task(osv.osv):
_log_create = True
_date_name = "date_start"
- def _read_group_type_id(self, cr, uid, ids, domain, context=None):
- context = context or {}
- stage_obj = self.pool.get('project.task.type')
- stage_ids = stage_obj.search(cr, uid, ['|',('id','in',ids)] + [('project_default','=',1)], context=context)
- return stage_obj.name_get(cr, uid, stage_ids, context=context)
- def _read_group_user_id(self, cr, uid, ids, domain, context={}):
- context = context or {}
- if type(context.get('project_id', None)) not in (int, long):
- return None
- proj = self.pool.get('project.project').browse(cr, uid, context['project_id'], context=context)
- ids += map(lambda x: x.id, proj.members)
- stage_obj = self.pool.get('res.users')
- stage_ids = stage_obj.search(cr, uid, [('id','in',ids)], context=context)
- return stage_obj.name_get(cr, uid, ids, context=context)
+ def _resolve_project_id_from_context(self, cr, uid, context=None):
+ """Return ID of project based on the value of 'project_id'
+ context key, or None if it cannot be resolved to a single project.
+ """
+ if context is None: context = {}
+ if type(context.get('project_id')) in (int, long):
+ project_id = context['project_id']
+ return project_id
+ if isinstance(context.get('project_id'), basestring):
+ project_name = context['project_id']
+ project_ids = self.pool.get('project.project').name_search(cr, uid, name=project_name)
+ if len(project_ids) == 1:
+ return project_ids[0][0]
+
+ def _read_group_type_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
+ stage_obj = self.pool.get('project.task.type')
+ project_id = self._resolve_project_id_from_context(cr, uid, context=context)
+ order = stage_obj._order
+ access_rights_uid = access_rights_uid or uid
+ if read_group_order == 'type_id desc':
+ # lame way to allow reverting search, should just work in the trivial case
+ order = '%s desc' % order
+ if project_id:
+ domain = ['|', ('id','in',ids), ('project_ids','in',project_id)]
+ else:
+ domain = ['|', ('id','in',ids), ('project_default','=',1)]
+ stage_ids = stage_obj._search(cr, uid, domain, order=order, access_rights_uid=access_rights_uid, context=context)
+ result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
+ # restore order of the search
+ result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
+ return result
+
+ def _read_group_user_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
+ res_users = self.pool.get('res.users')
+ project_id = self._resolve_project_id_from_context(cr, uid, context=context)
+ access_rights_uid = access_rights_uid or uid
+ if project_id:
+ ids += self.pool.get('project.project').read(cr, access_rights_uid, project_id, ['members'], context=context)['members']
+ order = res_users._order
+ # lame way to allow reverting search, should just work in the trivial case
+ if read_group_order == 'user_id desc':
+ order = '%s desc' % order
+ # de-duplicate and apply search order
+ ids = res_users._search(cr, uid, [('id','in',ids)], order=order, access_rights_uid=access_rights_uid, context=context)
+ result = res_users.name_get(cr, access_rights_uid, ids, context=context)
+ # restore order of the search
+ result.sort(lambda x,y: cmp(ids.index(x[0]), ids.index(y[0])))
+ return result
_group_by_full = {
'type_id': _read_group_type_id,
@@ -557,7 +594,12 @@ class task(osv.osv):
'state': fields.selection([('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancelled', 'Cancelled')], 'State', readonly=True, required=True,
help='If the task is created the state is \'Draft\'.\n If the task is started, the state becomes \'In Progress\'.\n If review is needed the task is in \'Pending\' state.\
\n If the task is over, the states is set to \'Done\'.'),
- 'kanban_state': fields.selection([('blocked', 'Blocked'),('normal', 'Normal'),('done', 'Done')], 'Kanban State', readonly=True, required=False),
+ 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready To Pull')], 'Kanban State',
+ help="A task's kanban state indicates special situations affecting it:\n"
+ " * Normal is the default situation\n"
+ " * Blocked indicates something is preventing the progress of this task\n"
+ " * Ready To Pull indicates the task is ready to be pulled to the next stage",
+ readonly=True, required=False),
'create_date': fields.datetime('Create Date', readonly=True,select=True),
'date_start': fields.datetime('Starting Date',select=True),
'date_end': fields.datetime('Ending Date',select=True),
@@ -837,37 +879,39 @@ class task(osv.osv):
self.write(cr, uid, ids, {'state': 'draft'}, context=context)
return True
- def do_delegate(self, cr, uid, task_id, delegate_data={}, context=None):
+ def do_delegate(self, cr, uid, ids, delegate_data={}, context=None):
"""
Delegate Task to another users.
"""
- task = self.browse(cr, uid, task_id, context=context)
- self.copy(cr, uid, task.id, {
- 'name': delegate_data['name'],
- 'user_id': delegate_data['user_id'],
- 'planned_hours': delegate_data['planned_hours'],
- 'remaining_hours': delegate_data['planned_hours'],
- 'parent_ids': [(6, 0, [task.id])],
- 'state': 'draft',
- 'description': delegate_data['new_task_description'] or '',
- 'child_ids': [],
- 'work_ids': []
- }, context=context)
- newname = delegate_data['prefix'] or ''
- self.write(cr, uid, [task.id], {
- 'remaining_hours': delegate_data['planned_hours_me'],
- 'planned_hours': delegate_data['planned_hours_me'] + (task.effective_hours or 0.0),
- 'name': newname,
- }, context=context)
- if delegate_data['state'] == 'pending':
- self.do_pending(cr, uid, [task.id], context)
- else:
- self.do_close(cr, uid, [task.id], context=context)
- user_pool = self.pool.get('res.users')
- delegate_user = user_pool.browse(cr, uid, delegate_data['user_id'], context=context)
- message = _("The task '%s' has been delegated to %s.") % (delegate_data['name'], delegate_user.name)
- self.log(cr, uid, task.id, message)
- return True
+ assert delegate_data['user_id'], _("Delegated User should be specified")
+ delegrated_tasks = {}
+ for task in self.browse(cr, uid, ids, context=context):
+ delegrated_task_id = self.copy(cr, uid, task.id, {
+ 'name': delegate_data['name'],
+ 'project_id': delegate_data['project_id'] and delegate_data['project_id'][0] or False,
+ 'user_id': delegate_data['user_id'] and delegate_data['user_id'][0] or False,
+ 'planned_hours': delegate_data['planned_hours'] or 0.0,
+ 'parent_ids': [(6, 0, [task.id])],
+ 'state': 'draft',
+ 'description': delegate_data['new_task_description'] or '',
+ 'child_ids': [],
+ 'work_ids': []
+ }, context=context)
+ newname = delegate_data['prefix'] or ''
+ task.write({
+ 'remaining_hours': delegate_data['planned_hours_me'],
+ 'planned_hours': delegate_data['planned_hours_me'] + (task.effective_hours or 0.0),
+ 'name': newname,
+ }, context=context)
+ if delegate_data['state'] == 'pending':
+ self.do_pending(cr, uid, task.id, context=context)
+ elif delegate_data['state'] == 'done':
+ self.do_close(cr, uid, task.id, context=context)
+
+ message = _("The task '%s' has been delegated to %s.") % (delegate_data['name'], delegate_data['user_id'][1])
+ self.log(cr, uid, task.id, message)
+ delegrated_tasks[task.id] = delegrated_task_id
+ return delegrated_tasks
def do_pending(self, cr, uid, ids, context={}):
self.write(cr, uid, ids, {'state': 'pending'}, context=context)
@@ -930,6 +974,20 @@ class task(osv.osv):
def prev_type(self, cr, uid, ids, *args):
return self._change_type(cr, uid, ids, False, *args)
+ # Overridden to reset the kanban_state to normal whenever
+ # the stage (type_id) of the task changes.
+ def write(self, cr, uid, ids, vals, context=None):
+ if isinstance(ids, (int, long)):
+ ids = [ids]
+ if vals and not 'kanban_state' in vals and 'type_id' in vals:
+ new_stage = vals.get('type_id')
+ vals_reset_kstate = dict(vals, kanban_state='normal')
+ for t in self.browse(cr, uid, ids, context=context):
+ write_vals = vals_reset_kstate if t.type_id != new_stage else vals
+ super(task,self).write(cr, uid, [t.id], write_vals, context=context)
+ return True
+ return super(task,self).write(cr, uid, ids, vals, context=context)
+
def unlink(self, cr, uid, ids, context=None):
if context == None:
context = {}
diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml
index d78f208526a..a3bfe9be673 100644
--- a/addons/project/project_data.xml
+++ b/addons/project/project_data.xml
@@ -1,30 +1,12 @@
-
-
-
-
- Project
- project.project
-
-
-
- Project task
- project.task
-
-
-
-
+
+
Projects
3
-
-
+
@@ -47,7 +29,5 @@
Deployment
-
-
diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml
index ac4bc14c701..7592d24a145 100644
--- a/addons/project/project_demo.xml
+++ b/addons/project/project_demo.xml
@@ -210,7 +210,7 @@
Documentation
- 06-02-2011
+ 2011-02-06
@@ -241,7 +241,6 @@
Training and Presentation
- 09-21-2011
diff --git a/addons/project/wizard/project_task_delegate.py b/addons/project/wizard/project_task_delegate.py
index eaf5dea3084..0b4457d5e7f 100644
--- a/addons/project/wizard/project_task_delegate.py
+++ b/addons/project/wizard/project_task_delegate.py
@@ -31,14 +31,23 @@ class project_task_delegate(osv.osv_memory):
_columns = {
'name': fields.char('Delegated Title', size=64, required=True, help="New title of the task delegated to the user"),
- 'prefix': fields.char('Your Task Title', size=64, required=True, help="Title for your validation task"),
+ 'prefix': fields.char('Your Task Title', size=64, help="Title for your validation task"),
+ 'project_id': fields.many2one('project.project', 'Project', help="User you want to delegate this task to"),
'user_id': fields.many2one('res.users', 'Assign To', required=True, help="User you want to delegate this task to"),
'new_task_description': fields.text('New Task Description', help="Reinclude the description of the task in the task of the user"),
'planned_hours': fields.float('Planned Hours', help="Estimated time to close this task by the delegated user"),
- 'planned_hours_me': fields.float('Hours to Validate', required=True, help="Estimated time for you to validate the work done by the user to whom you delegate this task"),
- 'state': fields.selection([('pending','Pending'), ('done','Done'), ], 'Validation State', required=True, help="New state of your own task. Pending will be reopened automatically when the delegated task is closed")
+ 'planned_hours_me': fields.float('Hours to Validate', help="Estimated time for you to validate the work done by the user to whom you delegate this task"),
+ 'state': fields.selection([('pending','Pending'), ('done','Done'), ], 'Validation State', help="New state of your own task. Pending will be reopened automatically when the delegated task is closed")
}
+ def onchange_project_id(self, cr, uid, ids, project_id=False, context=None):
+ project_project = self.pool.get('project.project')
+ if not project_id:
+ return {'value':{'user_id': False}}
+ project = project_project.browse(cr, uid, project_id, context=context)
+ return {'value': {'user_id': project.user_id and project.user_id.id or False}}
+
+
def default_get(self, cr, uid, fields, context=None):
"""
This function gets default values
@@ -47,10 +56,15 @@ class project_task_delegate(osv.osv_memory):
if context is None:
context = {}
record_id = context and context.get('active_id', False) or False
+ if not record_id:
+ return res
task_pool = self.pool.get('project.task')
task = task_pool.browse(cr, uid, record_id, context=context)
task_name =tools.ustr(task.name)
+ if 'project_id' in fields:
+ res.update({'project_id': task.project_id and task.project_id.id})
+
if 'name' in fields:
if task_name.startswith(_('CHECK: ')):
newname = tools.ustr(task_name).replace(_('CHECK: '), '')
@@ -105,9 +119,17 @@ class project_task_delegate(osv.osv_memory):
task_id = context.get('active_id', False)
task_pool = self.pool.get('project.task')
delegate_data = self.read(cr, uid, ids, context=context)[0]
- delegate_data['user_id'] = delegate_data['user_id'][0]
- delegate_data['name'] = tools.ustr(delegate_data['name'])
- task_pool.do_delegate(cr, uid, task_id, delegate_data, context=context)
- return {'type': 'ir.actions.act_window_close'}
+ delegated_tasks = task_pool.do_delegate(cr, uid, [task_id], delegate_data, context=context)
+ models_data = self.pool.get('ir.model.data')
+
+ action_model, action_id = models_data.get_object_reference(cr, uid, 'project', 'action_view_task')
+ view_model, task_view_form_id = models_data.get_object_reference(cr, uid, 'project', 'view_task_form2')
+ view_model, task_view_tree_id = models_data.get_object_reference(cr, uid, 'project', 'view_task_tree2')
+ action = self.pool.get(action_model).read(cr, uid, action_id, context=context)
+ action['res_id'] = delegated_tasks[task_id]
+ action['view_id'] = False
+ action['views'] = [(task_view_form_id, 'form'), (task_view_tree_id, 'tree')]
+ action['help'] = False
+ return action
project_task_delegate()
diff --git a/addons/project/wizard/project_task_delegate_view.xml b/addons/project/wizard/project_task_delegate_view.xml
index f9b27af05b3..072325c2b53 100644
--- a/addons/project/wizard/project_task_delegate_view.xml
+++ b/addons/project/wizard/project_task_delegate_view.xml
@@ -8,18 +8,19 @@
form
'''
+ search_extended += ''' \n'''
+ search_extended +=''' '''
+
+ res['arch'] = res['arch'].replace(' ', search_extended)
- res['arch'] = unicode(res['arch'], 'utf8').replace('', search_extended)
- attrs_sel = self.pool.get('project.gtd.context').name_search(cr, uid, '', [], context=context)
- context_id_info = self.pool.get('project.task').fields_get(cr, uid, ['context_id'], context=context)
- context_id_info['context_id']['selection'] = attrs_sel
- res['fields'].update(context_id_info)
return res
project_task()
diff --git a/addons/project_gtd/project_gtd_data.xml b/addons/project_gtd/project_gtd_data.xml
index 659b0fe9cb2..7d495023f87 100644
--- a/addons/project_gtd/project_gtd_data.xml
+++ b/addons/project_gtd/project_gtd_data.xml
@@ -18,10 +18,6 @@
This Week
terp-go-week
-
- This Month
- terp-go-month
-
Long Term
terp-project
diff --git a/addons/project_gtd/project_gtd_view.xml b/addons/project_gtd/project_gtd_view.xml
index a9166f7fbfa..66e7a8a69e3 100644
--- a/addons/project_gtd/project_gtd_view.xml
+++ b/addons/project_gtd/project_gtd_view.xml
@@ -82,7 +82,7 @@
-
+
@@ -101,11 +101,42 @@
-
+
+ project.task.gtd.search
+ project.task
+ search
+ 50
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- Todo List
+ My Tasks
project.task
- {'set_editable':True,'set_visible':True,'gtd':True,'user_invisible':True, 'search_default_user_id':uid, "search_default_draft": 1, "search_default_open": 1}
+
+ {'set_editable':True,'set_visible':True,'gtd':True,'user_invisible':True, "search_default_open": 1}
+ [('user_id','=',uid)]
form
tree,form,calendar,gantt,graph,kanban
diff --git a/addons/project_gtd/test/gtd_test.yml b/addons/project_gtd/test/gtd_test.yml
index 5c05dfd76cc..0583bc7e8e0 100644
--- a/addons/project_gtd/test/gtd_test.yml
+++ b/addons/project_gtd/test/gtd_test.yml
@@ -1,5 +1,5 @@
-
- Create a task 'Develop time management module' with monthly timebox
+ Create a task 'Develop time management module' with weekly timebox
-
!record {model: project.task, id: project_task_developtimemanagementmodule0}:
name: Develop time management module
@@ -7,7 +7,7 @@
project_id: project.project_project_22
remaining_hours: 15.0
state: draft
- timebox_id: project_gtd.timebox_monthly
+ timebox_id: project_gtd.timebox_weekly
-
@@ -36,12 +36,12 @@
sequence: 0.0
-
- Planify the task from monthly timebox to tomorrow timebox
+ Planify the task from weekly timebox to tomorrow timebox
-
!record {model: project.timebox.fill.plan, id: project_timebox_fill_plan_0}:
task_ids:
- project_task_developtimemanagementmodule0
- timebox_id: project_gtd.timebox_monthly
+ timebox_id: project_gtd.timebox_weekly
timebox_to_id: project_gtd_timebox_tomorrow1
@@ -75,4 +75,4 @@
Check task 'Develop time management module' is no more in tomorrow timebox
-
!assert {model: project.task, id: project_task_developtimemanagementmodule0, string: Task is not in tomorrow timebox}:
- - timebox_id.id != ref("project_gtd_timebox_tomorrow1")
\ No newline at end of file
+ - timebox_id.id != ref("project_gtd_timebox_tomorrow1")
diff --git a/addons/project_gtd/wizard/project_gtd_empty_view.xml b/addons/project_gtd/wizard/project_gtd_empty_view.xml
index b7ebeb1bd00..e66161b798f 100644
--- a/addons/project_gtd/wizard/project_gtd_empty_view.xml
+++ b/addons/project_gtd/wizard/project_gtd_empty_view.xml
@@ -31,7 +31,6 @@
-
Empty Timebox
client_action_multi
@@ -41,4 +40,4 @@
-
\ No newline at end of file
+
diff --git a/addons/project_gtd/wizard/project_gtd_fill_view.xml b/addons/project_gtd/wizard/project_gtd_fill_view.xml
index 41b99eb1b1e..9f9e080fc4e 100644
--- a/addons/project_gtd/wizard/project_gtd_fill_view.xml
+++ b/addons/project_gtd/wizard/project_gtd_fill_view.xml
@@ -33,7 +33,6 @@
-
Plannify Timebox
client_action_multi
@@ -43,4 +42,4 @@
-
\ No newline at end of file
+
diff --git a/addons/project_issue/board_project_issue_view.xml b/addons/project_issue/board_project_issue_view.xml
index cfe285134a7..2d30b2673b4 100644
--- a/addons/project_issue/board_project_issue_view.xml
+++ b/addons/project_issue/board_project_issue_view.xml
@@ -57,22 +57,16 @@
form
@@ -115,6 +109,7 @@
+
@@ -151,11 +146,11 @@
form
-
-
+
+
-
-
+
+
diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py
index 0833cab7441..6973002be23 100644
--- a/addons/project_issue/project_issue.py
+++ b/addons/project_issue/project_issue.py
@@ -48,6 +48,13 @@ class project_issue(crm.crm_case, osv.osv):
_order = "priority, create_date desc"
_inherit = ['mail.thread']
+ def write(self, cr, uid, ids, vals, context=None):
+ #Update last action date everytime the user change the stage, the state or send a new email
+ logged_fields = ['type_id', 'state', 'message_ids']
+ if any([field in vals for field in logged_fields]):
+ vals['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S')
+ return super(project_issue, self).write(cr, uid, ids, vals, context)
+
def case_open(self, cr, uid, ids, *args):
"""
@param self: The object pointer
diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml
index a54ae14737d..6bd0d208fd4 100644
--- a/addons/project_issue/project_issue_view.xml
+++ b/addons/project_issue/project_issue_view.xml
@@ -197,6 +197,7 @@
+
diff --git a/addons/project_long_term/project_long_term_data.xml b/addons/project_long_term/project_long_term_data.xml
index 8da591f1337..ebc084668b7 100644
--- a/addons/project_long_term/project_long_term_data.xml
+++ b/addons/project_long_term/project_long_term_data.xml
@@ -1,16 +1,5 @@
-
-
-
- Project
- project.project
-
-
- Project task
- project.task
-
-
diff --git a/addons/project_long_term/project_long_term_view.xml b/addons/project_long_term/project_long_term_view.xml
index 577ce6d135c..cdeb0a0d63d 100644
--- a/addons/project_long_term/project_long_term_view.xml
+++ b/addons/project_long_term/project_long_term_view.xml
@@ -377,7 +377,7 @@
+ name="Resources" parent="project.menu_definitions" sequence="3"/>
diff --git a/addons/project_mrp/project_mrp.py b/addons/project_mrp/project_mrp.py
index ea121033b5b..3bbb0658b11 100644
--- a/addons/project_mrp/project_mrp.py
+++ b/addons/project_mrp/project_mrp.py
@@ -57,6 +57,12 @@ product_product()
class sale_order(osv.osv):
_inherit ='sale.order'
+ def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, *args):
+ proc_data = super(sale_order, self)._prepare_order_line_procurement(cr,
+ uid, order, line, move_id, date_planned, *args)
+ proc_data['sale_line_id'] = line.id
+ return proc_data
+
def _picked_rate(self, cr, uid, ids, name, arg, context=None):
if not ids:
return {}
diff --git a/addons/project_mrp/test/project_task_procurement.yml b/addons/project_mrp/test/project_task_procurement.yml
index faf99dc5419..b2cc6a68363 100644
--- a/addons/project_mrp/test/project_task_procurement.yml
+++ b/addons/project_mrp/test/project_task_procurement.yml
@@ -1,10 +1,11 @@
--
- In order to test project_mrp module with OpenERP I want to create sale orders
- with product type 'service' so when procurement runs one task is created.
+- |
+ In order to test project_mrp module with OpenERP I create a sale order
+ with product type 'service' so when procurement runs one task is created for
+ the project associated with my sale order.
-
I create record for a service type product.
-
- !record {model: product.product, id: product_product_partnerstraining0}:
+ !record {model: product.product, id: product_product_partnerstraining0, view: False}:
categ_id: product.product_category_7
cost_method: standard
mes_type: fixed
@@ -15,8 +16,9 @@
uom_id: product.uom_day
uom_po_id: product.uom_day
warranty: 0.0
--
- I create a sale order for product Partners Training which has type 'Service'.
+- |
+ I create a sale order for product Partners Training which has type 'Service',
+ and select the appropriate Analytic Account matching my project.
-
!record {model: sale.order, id: sale_order_so0}:
date_order: !eval time.strftime('%Y-%m-%d')
@@ -40,6 +42,13 @@
pricelist_id: product.list0
shop_id: sale.shop
user_id: base.user_demo
+
+- I select the Analytic Account for my project on the sale order
+-
+ !python {model: sale.order}: |
+ acc_id = self.pool.get('project.project').browse(cr, uid,
+ ref('project.project_integrate_openerp')).analytic_account_id.id
+ self.write(cr, uid, ref('sale_order_so0'), {'project_id': acc_id})
-
I confirm this sale order.
-
@@ -60,11 +69,15 @@
- model: procurement.order
search: "[]"
-
- Now I check that one task is created for my sale order.
+ Now I check that one task is created for my sale order, in the desired project
-
!python {model: project.task}: |
order_obj = self.pool.get('sale.order')
- order = order_obj.browse(cr, uid, ref('sale_order_so0'))
- task_id = self.search(cr, uid, [('name','like', order.name)])
- assert task_id, 'No Task is created!'
-
+ order = order_obj.browse(cr, uid, ref('sale_order_so0'))
+ # planned_hours == 40 because default company project UOM is hours, and
+ # product was sold as 5.0 days.
+ task_id = self.search(cr, uid, [('name', '=', order.name+":Partners Training"),
+ ('project_id','=', ref('project.project_integrate_openerp')),
+ ('planned_hours','=', 40.0),
+ ('state','=','draft')])
+ assert task_id, 'Expected Task not found!'
\ No newline at end of file
diff --git a/addons/project_planning/board_project_planning_view.xml b/addons/project_planning/board_project_planning_view.xml
index 867dba5767f..db29a7f067a 100644
--- a/addons/project_planning/board_project_planning_view.xml
+++ b/addons/project_planning/board_project_planning_view.xml
@@ -5,11 +5,11 @@
board.project.planning.form
board.board
-
+
form
-
-
+
+
diff --git a/addons/project_retro_planning/i18n/oc.po b/addons/project_retro_planning/i18n/oc.po
new file mode 100644
index 00000000000..7451fd7b147
--- /dev/null
+++ b/addons/project_retro_planning/i18n/oc.po
@@ -0,0 +1,47 @@
+# Occitan (post 1500) 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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \n"
+"POT-Creation-Date: 2011-01-11 11:15+0000\n"
+"PO-Revision-Date: 2011-11-20 09:25+0000\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: Occitan (post 1500) \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2011-11-21 05:22+0000\n"
+"X-Generator: Launchpad (build 14299)\n"
+
+#. module: project_retro_planning
+#: model:ir.model,name:project_retro_planning.model_project_project
+msgid "Project"
+msgstr "Projècte"
+
+#. module: project_retro_planning
+#: model:ir.module.module,shortdesc:project_retro_planning.module_meta_information
+msgid "Project Retro planning"
+msgstr "Retroplanificacion de projèctes"
+
+#. module: project_retro_planning
+#: model:ir.module.module,description:project_retro_planning.module_meta_information
+msgid ""
+"\n"
+" - If end date of project is changed\n"
+" then the deadline date and start date for all the tasks will "
+"change accordingly "
+msgstr ""
+
+#. module: project_retro_planning
+#: constraint:project.project:0
+msgid "Error! project start-date must be lower then project end-date."
+msgstr ""
+
+#. module: project_retro_planning
+#: constraint:project.project:0
+msgid "Error! You cannot assign escalation to the same project!"
+msgstr ""
diff --git a/addons/project_scrum/board_project_scrum_view.xml b/addons/project_scrum/board_project_scrum_view.xml
index 5620950fa5a..2a9d3532b2d 100644
--- a/addons/project_scrum/board_project_scrum_view.xml
+++ b/addons/project_scrum/board_project_scrum_view.xml
@@ -76,15 +76,15 @@
form
@@ -133,8 +133,8 @@
form
-
-
+
+
diff --git a/addons/project_scrum/project_scrum_view.xml b/addons/project_scrum/project_scrum_view.xml
index ddf74eb2e93..7749b5e2d3b 100644
--- a/addons/project_scrum/project_scrum_view.xml
+++ b/addons/project_scrum/project_scrum_view.xml
@@ -473,7 +473,6 @@
View sprint tasks
-
@@ -488,7 +487,6 @@
View sprint backlog
-
-
+
+
diff --git a/addons/purchase/__init__.py b/addons/purchase/__init__.py
index 301e32ebca9..66f92a60831 100644
--- a/addons/purchase/__init__.py
+++ b/addons/purchase/__init__.py
@@ -26,6 +26,7 @@ import wizard
import report
import stock
import company
+import edi
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py
index 901fb6d3e98..b4f3a9adf30 100644
--- a/addons/purchase/__openerp__.py
+++ b/addons/purchase/__openerp__.py
@@ -40,7 +40,7 @@ Dashboard for purchase management that includes:
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'images' : ['images/purchase_order.jpeg', 'images/purchase_analysis.jpeg', 'images/request_for_quotation.jpeg'],
- 'depends': ['base', 'account', 'stock', 'process', 'procurement'],
+ 'depends': ['stock', 'process', 'procurement'],
'data': [
'security/purchase_security.xml',
'security/ir.model.access.csv',
@@ -58,15 +58,17 @@ Dashboard for purchase management that includes:
'process/purchase_process.xml',
'report/purchase_report_view.xml',
'board_purchase_view.xml',
+ 'edi/purchase_order_action_data.xml',
],
'test': [
- 'test/process/cancel_order.yml',
+ 'test/process/cancel_order.yml',
'test/process/rfq2order2done.yml',
'test/process/generate_invoice_from_reception.yml',
- 'test/process/run_scheduler.yml',
- 'test/process/merge_order.yml',
+ 'test/process/run_scheduler.yml',
+ 'test/process/merge_order.yml',
+ 'test/process/edi_purchase_order.yml',
'test/ui/print_report.yml',
- 'test/ui/duplicate_order.yml',
+ 'test/ui/duplicate_order.yml',
'test/ui/delete_order.yml',
],
'demo': [
diff --git a/addons/purchase/board_purchase_view.xml b/addons/purchase/board_purchase_view.xml
index 8a289ccbedb..453f029aa0d 100644
--- a/addons/purchase/board_purchase_view.xml
+++ b/addons/purchase/board_purchase_view.xml
@@ -14,7 +14,6 @@
form
tree,form
[('date_order','>',time.strftime('%Y-01-01 00:00:00')),('date_order','<',time.strftime('%Y-12-31 23:59:59')), ('state','=','draft')]
-
Purchase Order Waiting Approval
@@ -23,7 +22,6 @@
form
tree,form
[('date_order','>',time.strftime('%Y-01-01 00:00:00')),('date_order','<',time.strftime('%Y-12-31 23:59:59')), ('state','in',('wait','confirmed'))]
-
@@ -131,25 +129,25 @@
form
diff --git a/addons/purchase/edi/__init__.py b/addons/purchase/edi/__init__.py
new file mode 100644
index 00000000000..cfac0182d76
--- /dev/null
+++ b/addons/purchase/edi/__init__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Business Applications
+# Copyright (c) 2011 OpenERP S.A.
+#
+# 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 .
+#
+##############################################################################
+
+import purchase_order
\ No newline at end of file
diff --git a/addons/purchase/edi/purchase_order.py b/addons/purchase/edi/purchase_order.py
new file mode 100644
index 00000000000..235b02645ed
--- /dev/null
+++ b/addons/purchase/edi/purchase_order.py
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Business Applications
+# Copyright (c) 2011 OpenERP S.A.
+#
+# 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 .
+#
+##############################################################################
+
+from datetime import datetime, timedelta
+from dateutil.relativedelta import relativedelta
+
+from osv import fields, osv, orm
+from edi import EDIMixin
+from tools import DEFAULT_SERVER_DATE_FORMAT
+from tools.translate import _
+
+PURCHASE_ORDER_LINE_EDI_STRUCT = {
+ 'name': True,
+ 'date_planned': True,
+ 'product_id': True,
+ 'product_uom': True,
+ 'price_unit': True,
+ 'product_qty': True,
+ 'notes': True,
+
+ # fields used for web preview only - discarded on import
+ 'price_subtotal': True,
+}
+
+PURCHASE_ORDER_EDI_STRUCT = {
+ 'company_id': True, # -> to be changed into partner
+ 'name': True,
+ 'partner_ref': True,
+ 'origin': True,
+ 'date_order': True,
+ 'partner_id': True,
+ #custom: 'partner_address',
+ 'notes': True,
+ 'order_line': PURCHASE_ORDER_LINE_EDI_STRUCT,
+ #custom: currency_id
+
+ # fields used for web preview only - discarded on import
+ 'amount_total': True,
+ 'amount_untaxed': True,
+ 'amount_tax': True,
+}
+
+class purchase_order(osv.osv, EDIMixin):
+ _inherit = 'purchase.order'
+
+ def edi_export(self, cr, uid, records, edi_struct=None, context=None):
+ """Exports a purchase order"""
+ edi_struct = dict(edi_struct or PURCHASE_ORDER_EDI_STRUCT)
+ res_company = self.pool.get('res.company')
+ res_partner_address = self.pool.get('res.partner.address')
+ edi_doc_list = []
+ for order in records:
+ # generate the main report
+ self._edi_generate_report_attachment(cr, uid, order, context=context)
+
+ # Get EDI doc based on struct. The result will also contain all metadata fields and attachments.
+ edi_doc = super(purchase_order,self).edi_export(cr, uid, [order], edi_struct, context)[0]
+ edi_doc.update({
+ # force trans-typing to purchase.order upon import
+ '__import_model': 'sale.order',
+ '__import_module': 'sale',
+
+ 'company_address': res_company.edi_export_address(cr, uid, order.company_id, context=context),
+ 'partner_address': res_partner_address.edi_export(cr, uid, [order.partner_address_id], context=context)[0],
+ 'currency': self.pool.get('res.currency').edi_export(cr, uid, [order.pricelist_id.currency_id],
+ context=context)[0],
+ })
+ if edi_doc.get('order_line'):
+ for line in edi_doc['order_line']:
+ line['__import_model'] = 'sale.order.line'
+ edi_doc_list.append(edi_doc)
+ return edi_doc_list
+
+ def edi_import_company(self, cr, uid, edi_document, context=None):
+ # TODO: for multi-company setups, we currently import the document in the
+ # user's current company, but we should perhaps foresee a way to select
+ # the desired company among the user's allowed companies
+
+ self._edi_requires_attributes(('company_id','company_address'), edi_document)
+ res_partner_address = self.pool.get('res.partner.address')
+ res_partner = self.pool.get('res.partner')
+
+ # imported company = as a new partner
+ src_company_id, src_company_name = edi_document.pop('company_id')
+ partner_id = self.edi_import_relation(cr, uid, 'res.partner', src_company_name,
+ src_company_id, context=context)
+ partner_value = {'customer': True}
+ res_partner.write(cr, uid, [partner_id], partner_value, context=context)
+
+ # imported company_address = new partner address
+ address_info = edi_document.pop('company_address')
+ address_info['partner_id'] = (src_company_id, src_company_name)
+ address_info['type'] = 'default'
+ address_id = res_partner_address.edi_import(cr, uid, address_info, context=context)
+
+ # modify edi_document to refer to new partner/address
+ partner_address = res_partner_address.browse(cr, uid, address_id, context=context)
+ edi_document['partner_id'] = (src_company_id, src_company_name)
+ edi_document.pop('partner_address', False) # ignored
+ edi_document['partner_address_id'] = self.edi_m2o(cr, uid, partner_address, context=context)
+
+ return partner_id
+
+ def _edi_get_pricelist(self, cr, uid, partner_id, currency, context=None):
+ # TODO: refactor into common place for purchase/sale, e.g. into product module
+ partner_model = self.pool.get('res.partner')
+ partner = partner_model.browse(cr, uid, partner_id, context=context)
+ pricelist = partner.property_product_pricelist_purchase
+ if not pricelist:
+ pricelist = self.pool.get('ir.model.data').get_object(cr, uid, 'purchase', 'list0', context=context)
+
+ if not pricelist.currency_id == currency:
+ # look for a pricelist with the right type and currency, or make a new one
+ pricelist_type = 'purchase'
+ product_pricelist = self.pool.get('product.pricelist')
+ match_pricelist_ids = product_pricelist.search(cr, uid,[('type','=',pricelist_type),
+ ('currency_id','=',currency.id)])
+ if match_pricelist_ids:
+ pricelist_id = match_pricelist_ids[0]
+ else:
+ pricelist_name = _('EDI Pricelist (%s)') % (currency.name,)
+ pricelist_id = product_pricelist.create(cr, uid, {'name': pricelist_name,
+ 'type': pricelist_type,
+ 'currency_id': currency.id,
+ })
+ self.pool.get('product.pricelist.version').create(cr, uid, {'name': pricelist_name,
+ 'pricelist_id': pricelist_id})
+ pricelist = product_pricelist.browse(cr, uid, pricelist_id)
+
+ return self.edi_m2o(cr, uid, pricelist, context=context)
+
+ def _edi_get_location(self, cr, uid, partner_id, context=None):
+ partner_model = self.pool.get('res.partner')
+ partner = partner_model.browse(cr, uid, partner_id, context=context)
+ location = partner.property_stock_customer
+ if not location:
+ location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock', context=context)
+ return self.edi_m2o(cr, uid, location, context=context)
+
+ def edi_import(self, cr, uid, edi_document, context=None):
+ self._edi_requires_attributes(('company_id','company_address','order_line','date_order','currency'), edi_document)
+
+ #import company as a new partner
+ partner_id = self.edi_import_company(cr, uid, edi_document, context=context)
+
+ # currency for rounding the discount calculations and for the pricelist
+ res_currency = self.pool.get('res.currency')
+ currency_info = edi_document.pop('currency')
+ currency_id = res_currency.edi_import(cr, uid, currency_info, context=context)
+ order_currency = res_currency.browse(cr, uid, currency_id)
+
+ partner_ref = edi_document.pop('partner_ref', False)
+ edi_document['partner_ref'] = edi_document['name']
+ edi_document['name'] = partner_ref or edi_document['name']
+ edi_document['pricelist_id'] = self._edi_get_pricelist(cr, uid, partner_id, order_currency, context=context)
+ edi_document['location_id'] = self._edi_get_location(cr, uid, partner_id, context=context)
+
+ # discard web preview fields, if present
+ edi_document.pop('amount_total', None)
+ edi_document.pop('amount_tax', None)
+ edi_document.pop('amount_untaxed', None)
+ edi_document.pop('payment_term', None)
+ edi_document.pop('order_policy', None)
+ edi_document.pop('user_id', None)
+
+ for order_line in edi_document['order_line']:
+ self._edi_requires_attributes(('date_planned', 'product_id', 'product_uom', 'product_qty', 'price_unit'), order_line)
+ # original sale order contains unit price and discount, but not final line price
+ discount = order_line.pop('discount', 0.0)
+ if discount:
+ order_line['price_unit'] = res_currency.round(cr, uid, order_currency,
+ (order_line['price_unit'] * (1 - (discount or 0.0) / 100.0)))
+ # sale order lines have sequence numbers, not purchase order lines
+ order_line.pop('sequence', None)
+
+ # discard web preview fields, if present
+ order_line.pop('price_subtotal', None)
+ return super(purchase_order,self).edi_import(cr, uid, edi_document, context=context)
+
+class purchase_order_line(osv.osv, EDIMixin):
+ _inherit='purchase.order.line'
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml
new file mode 100644
index 00000000000..f3f610b8e75
--- /dev/null
+++ b/addons/purchase/edi/purchase_order_action_data.xml
@@ -0,0 +1,156 @@
+
+
+
+
+
+ if not object.partner_id.opt_out: object.edi_export_and_email(template_ext_id='purchase.email_template_edi_purchase', context=context)
+ code
+ ir.actions.server
+
+ True
+ Auto-email confirmed purchase orders
+
+
+
+
+ Email Templates
+ email.template
+ form
+ form,tree
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Automated Purchase Order Notification Mail
+ ${object.validator.user_email or ''}
+ ${object.company_id.name} Order (Ref ${object.name or 'n/a' })
+ ${object.partner_address_id.email}
+
+
+
+
+ Hello${object.partner_address_id.name and ' ' or ''}${object.partner_address_id.name or ''},
+
+ Here is a purchase order confirmation from ${object.company_id.name}:
+
+
+ REFERENCES
+ Order number: ${object.name}
+ Order total: ${object.amount_total} ${object.pricelist_id.currency_id.name}
+ Order date: ${object.date_order}
+ % if object.origin:
+ Order reference: ${object.origin}
+ % endif
+ % if object.partner_ref:
+ Your reference: ${object.partner_ref}
+ % endif
+ Your contact: ${object.validator.name}
+
+
+
+ You can view the order confirmation document and download it using the following link:
+
+ View Order
+
+
+ If you have any question, do not hesitate to contact us.
+ Thank you!
+
+
+
+
+ ${object.company_id.name}
+
+
+
+ % if object.company_id.street:
+ ${object.company_id.street}
+ % endif
+ % if object.company_id.street2:
+ ${object.company_id.street2}
+ % endif
+ % if object.company_id.city or object.company_id.zip:
+ ${object.company_id.zip} ${object.company_id.city}
+ % endif
+ % if object.company_id.country_id:
+ ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif
+
+ % if object.company_id.phone:
+
+ Phone: ${object.company_id.phone}
+
+ % endif
+ % if object.company_id.website:
+
+ %endif
+
+
+
+ ]]>
+
+ % endif
+ | Your contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''}
+
+You can view the order confirmation and download it using the following link:
+ ${ctx.get('edi_web_url_view') or 'n/a'}
+
+If you have any question, do not hesitate to contact us.
+
+Thank you!
+
+
+--
+${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''}
+${object.company_id.name}
+% if object.company_id.street:
+${object.company_id.street or ''}
+% endif
+% if object.company_id.street2:
+${object.company_id.street2}
+% endif
+% if object.company_id.city or object.company_id.zip:
+${object.company_id.zip or ''} ${object.company_id.city or ''}
+% endif
+% if object.company_id.country_id:
+${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+% endif
+% if object.company_id.phone:
+Phone: ${object.company_id.phone}
+% endif
+% if object.company_id.website:
+${object.company_id.website or ''}
+% endif
+ ]]>
+
+
+
\ No newline at end of file
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index cdb60cce8e4..0dc015834a0 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -188,7 +188,7 @@ class purchase_order(osv.osv):
'shipped_rate': fields.function(_shipped_rate, string='Received', type='float'),
'invoiced': fields.function(_invoiced, string='Invoiced & Paid', type='boolean', help="It indicates that an invoice has been paid"),
'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'),
- 'invoice_method': fields.selection([('manual','Based on Purchase Order lines'),('order','Based on generated invoice'),('picking','Based on receptions')], 'Invoicing Control', required=True,
+ 'invoice_method': fields.selection([('manual','Based on Purchase Order lines'),('order','Based on generated draft invoice'),('picking','Based on receptions')], 'Invoicing Control', required=True,
help="Based on Purchase Order lines: place individual lines in 'Invoice Control > Based on P.O. lines' frow where you can selectively create an invoice.\n" \
"Based on generated invoice: create a draft invoice you can validate later.\n" \
"Based on receptions: let you create an invoice when receptions are validated."
@@ -227,7 +227,7 @@ class purchase_order(osv.osv):
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.order', context=c),
}
_sql_constraints = [
- ('name_uniq', 'unique(name)', 'Order Reference must be unique !'),
+ ('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
]
_name = "purchase.order"
_description = "Purchase Order"
@@ -319,17 +319,25 @@ class purchase_order(osv.osv):
'ref_partner_id': po.partner_id.id,
'ref_doc1': 'purchase.order,%d' % (po.id,),
})
- def inv_line_create(self, cr, uid, a, ol):
- return (0, False, {
- 'name': ol.name,
- 'account_id': a,
- 'price_unit': ol.price_unit or 0.0,
- 'quantity': ol.product_qty,
- 'product_id': ol.product_id.id or False,
- 'uos_id': ol.product_uom.id or False,
- 'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])],
- 'account_analytic_id': ol.account_analytic_id.id or False,
- })
+
+ def _prepare_inv_line(self, cr, uid, account_id, order_line, context=None):
+ """Collects require data from purchase order line that is used to create invoice line
+ for that purchase order line
+ :param account_id: Expense account of the product of PO line if any.
+ :param browse_record order_line: Purchase order line browse record
+ :return: Value for fields of invoice lines.
+ :rtype: dict
+ """
+ return {
+ 'name': order_line.name,
+ 'account_id': account_id,
+ 'price_unit': order_line.price_unit or 0.0,
+ 'quantity': order_line.product_qty,
+ 'product_id': order_line.product_id.id or False,
+ 'uos_id': order_line.product_uom.id or False,
+ 'invoice_line_tax_id': [(6, 0, [x.id for x in order_line.taxes_id])],
+ 'account_analytic_id': order_line.account_analytic_id.id or False,
+ }
def action_cancel_draft(self, cr, uid, ids, *args):
if not len(ids):
@@ -345,55 +353,71 @@ class purchase_order(osv.osv):
self.log(cr, uid, id, message)
return True
- #TOFIX
- # - implement hook method on create invoice and invoice line
- # - doc string
- def action_invoice_create(self, cr, uid, ids, *args):
+ def action_invoice_create(self, cr, uid, ids, context=None):
+ """Generates invoice for given ids of purchase orders and links that invoice ID to purchase order.
+ :param ids: list of ids of purchase orders.
+ :return: ID of created invoice.
+ :rtype: int
+ """
res = False
journal_obj = self.pool.get('account.journal')
- for o in self.browse(cr, uid, ids):
- il = []
- todo = []
- for ol in o.order_line:
- todo.append(ol.id)
- if ol.product_id:
- a = ol.product_id.product_tmpl_id.property_account_expense.id
- if not a:
- a = ol.product_id.categ_id.property_account_expense_categ.id
- if not a:
- raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % (ol.product_id.name, ol.product_id.id,))
- else:
- a = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category').id
- fpos = o.fiscal_position or False
- a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
- il.append(self.inv_line_create(cr, uid, a, ol))
+ inv_obj = self.pool.get('account.invoice')
+ inv_line_obj = self.pool.get('account.invoice.line')
+ fiscal_obj = self.pool.get('account.fiscal.position')
+ property_obj = self.pool.get('ir.property')
- a = o.partner_id.property_account_payable.id
- journal_ids = journal_obj.search(cr, uid, [('type', '=','purchase'),('company_id', '=', o.company_id.id)], limit=1)
+ for order in self.browse(cr, uid, ids, context=context):
+ pay_acc_id = order.partner_id.property_account_payable.id
+ journal_ids = journal_obj.search(cr, uid, [('type', '=','purchase'),('company_id', '=', order.company_id.id)], limit=1)
if not journal_ids:
raise osv.except_osv(_('Error !'),
- _('There is no purchase journal defined for this company: "%s" (id:%d)') % (o.company_id.name, o.company_id.id))
- inv = {
- 'name': o.partner_ref or o.name,
- 'reference': o.partner_ref or o.name,
- 'account_id': a,
+ _('There is no purchase journal defined for this company: "%s" (id:%d)') % (order.company_id.name, order.company_id.id))
+
+ # generate invoice line correspond to PO line and link that to created invoice (inv_id) and PO line
+ inv_lines = []
+ for po_line in order.order_line:
+ if po_line.product_id:
+ acc_id = po_line.product_id.product_tmpl_id.property_account_expense.id
+ if not acc_id:
+ acc_id = po_line.product_id.categ_id.property_account_expense_categ.id
+ if not acc_id:
+ raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % (po_line.product_id.name, po_line.product_id.id,))
+ else:
+ acc_id = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category').id
+ fpos = order.fiscal_position or False
+ acc_id = fiscal_obj.map_account(cr, uid, fpos, acc_id)
+
+ inv_line_data = self._prepare_inv_line(cr, uid, acc_id, po_line, context=context)
+ inv_line_id = inv_line_obj.create(cr, uid, inv_line_data, context=context)
+ inv_lines.append(inv_line_id)
+
+ po_line.write({'invoiced':True, 'invoice_lines': [(4, inv_line_id)]}, context=context)
+
+ # get invoice data and create invoice
+ inv_data = {
+ 'name': order.partner_ref or order.name,
+ 'reference': order.partner_ref or order.name,
+ 'account_id': pay_acc_id,
'type': 'in_invoice',
- 'partner_id': o.partner_id.id,
- 'currency_id': o.pricelist_id.currency_id.id,
- 'address_invoice_id': o.partner_address_id.id,
- 'address_contact_id': o.partner_address_id.id,
+ 'partner_id': order.partner_id.id,
+ 'currency_id': order.pricelist_id.currency_id.id,
+ 'address_invoice_id': order.partner_address_id.id,
+ 'address_contact_id': order.partner_address_id.id,
'journal_id': len(journal_ids) and journal_ids[0] or False,
- 'origin': o.name,
- 'invoice_line': il,
- 'fiscal_position': o.fiscal_position.id or o.partner_id.property_account_position.id,
- 'payment_term': o.partner_id.property_payment_term and o.partner_id.property_payment_term.id or False,
- 'company_id': o.company_id.id,
+ 'invoice_line': [(6, 0, inv_lines)],
+ 'origin': order.name,
+ 'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id,
+ 'payment_term': order.partner_id.property_payment_term and order.partner_id.property_payment_term.id or False,
+ 'company_id': order.company_id.id,
}
- inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice'})
- self.pool.get('account.invoice').button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, set_total=True)
- self.pool.get('purchase.order.line').write(cr, uid, todo, {'invoiced':True})
- self.write(cr, uid, [o.id], {'invoice_ids': [(4, inv_id)]})
+ inv_id = inv_obj.create(cr, uid, inv_data, context=context)
+
+ # compute the invoice
+ inv_obj.button_compute(cr, uid, [inv_id], context=context, set_total=True)
+
+ # Link this new invoice to related purchase order
+ order.write({'invoice_ids': [(4, inv_id)]}, context=context)
res = inv_id
return res
diff --git a/addons/purchase/purchase_order_demo.yml b/addons/purchase/purchase_order_demo.yml
index ba74d9e51f2..abfaa102033 100644
--- a/addons/purchase/purchase_order_demo.yml
+++ b/addons/purchase/purchase_order_demo.yml
@@ -1,5 +1,5 @@
-
- !record {model: purchase.order, id: order_purchase1, view: False}:
+ !record {model: purchase.order, id: order_purchase1}:
partner_id: base.res_partner_asus
invoice_method: order
order_line:
@@ -11,7 +11,7 @@
product_qty: 2.0
-
- !record {model: purchase.order, id: order_purchase2, view: False}:
+ !record {model: purchase.order, id: order_purchase2}:
partner_id: base.res_partner_3
invoice_method: picking
order_line:
@@ -19,7 +19,7 @@
price_unit: 900
-
- !record {model: purchase.order, id: order_purchase3, view: False}:
+ !record {model: purchase.order, id: order_purchase3}:
partner_id: base.res_partner_desertic_hispafuentes
order_line:
- product_id: product.product_product_0
@@ -30,7 +30,7 @@
product_qty: 5
-
- !record {model: purchase.order, id: order_purchase4, view: False}:
+ !record {model: purchase.order, id: order_purchase4}:
partner_id: base.res_partner_4
order_line:
- product_id: product.product_product_pc2
@@ -44,7 +44,7 @@
product_qty: 15
-
- !record {model: purchase.order, id: order_purchase5, view: False}:
+ !record {model: purchase.order, id: order_purchase5}:
partner_id: base.res_partner_maxtor
order_line:
- product_id: product.product_product_cpu1
@@ -55,7 +55,7 @@
product_qty: 10
-
- !record {model: purchase.order, id: order_purchase6, view: False}:
+ !record {model: purchase.order, id: order_purchase6}:
partner_id: base.res_partner_vickingdirect0
order_line:
- product_id: product.product_product_hdd2
@@ -66,7 +66,7 @@
product_qty: 10
-
- !record {model: purchase.order, id: order_purchase7, view: False}:
+ !record {model: purchase.order, id: order_purchase7}:
partner_id: base.res_partner_desertic_hispafuentes
order_line:
- product_id: product.product_product_0
diff --git a/addons/purchase/purchase_report.xml b/addons/purchase/purchase_report.xml
index e13ca9911b0..a3a4a6a70b6 100644
--- a/addons/purchase/purchase_report.xml
+++ b/addons/purchase/purchase_report.xml
@@ -1,7 +1,11 @@
-
-
+
+
diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml
index 49102d6bfdf..2b1ba34b8e1 100644
--- a/addons/purchase/purchase_view.xml
+++ b/addons/purchase/purchase_view.xml
@@ -90,12 +90,13 @@
-
+
+ parent="menu_procurement_management_inventory" sequence="11"
+ groups="base.group_extended"/>