mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 09:32:04 +02:00
Initial commit: Sale packages
This commit is contained in:
commit
14e3d26998
6469 changed files with 2479670 additions and 0 deletions
|
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import wizard
|
||||
from . import report
|
||||
|
||||
from odoo import api, SUPERUSER_ID
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
|
||||
env.ref("account.account_analytic_line_rule_billing_user").write({'domain_force': "[(1, '=', 1)]"})
|
||||
rule_readonly_user = env.ref("account.account_analytic_line_rule_readonly_user", raise_if_not_found=False)
|
||||
if rule_readonly_user:
|
||||
rule_readonly_user.write({'domain_force': "[(1, '=', 1)]"})
|
||||
|
||||
def _sale_timesheet_post_init(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
products = env['product.template'].search([('detailed_type', '=', 'service'), ('invoice_policy', '=', 'order'), ('service_type', '=', 'manual')])
|
||||
|
||||
for product in products:
|
||||
product.service_type = 'timesheet'
|
||||
product._compute_service_policy()
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Sales Timesheet',
|
||||
'category': 'Hidden',
|
||||
'summary': 'Sell based on timesheets',
|
||||
'description': """
|
||||
Allows to sell timesheets in your sales order
|
||||
=============================================
|
||||
|
||||
This module set the right product on all timesheet lines
|
||||
according to the order/contract you work on. This allows to
|
||||
have real delivered quantities in sales orders.
|
||||
""",
|
||||
'depends': ['sale_project', 'hr_timesheet'],
|
||||
'data': [
|
||||
'data/sale_service_data.xml',
|
||||
'report/timesheets_analysis_views.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/sale_timesheet_security.xml',
|
||||
'views/account_invoice_views.xml',
|
||||
'views/sale_order_views.xml',
|
||||
'views/product_views.xml',
|
||||
'views/project_task_views.xml',
|
||||
'views/project_update_templates.xml',
|
||||
'views/hr_timesheet_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/sale_timesheet_portal_templates.xml',
|
||||
'views/project_sharing_views.xml',
|
||||
'report/report_timesheet_templates.xml',
|
||||
'wizard/project_create_sale_order_views.xml',
|
||||
'wizard/project_create_invoice_views.xml',
|
||||
'wizard/sale_make_invoice_advance_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/sale_service_demo.xml',
|
||||
],
|
||||
'auto_install': True,
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
'assets': {
|
||||
'web.assets_frontend': [
|
||||
'sale_timesheet/static/src/scss/sale_timesheet_portal.scss',
|
||||
],
|
||||
'web.assets_backend': [
|
||||
'sale_timesheet/static/src/components/**/*',
|
||||
'sale_timesheet/static/src/js/so_line_one2many.js',
|
||||
'sale_timesheet/static/src/xml/**/*',
|
||||
],
|
||||
'web.assets_tests': [
|
||||
'sale_timesheet/static/tests/**/*',
|
||||
],
|
||||
},
|
||||
'license': 'LGPL-3',
|
||||
'post_init_hook': '_sale_timesheet_post_init',
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import portal
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from odoo import http, _
|
||||
from odoo.exceptions import AccessError, MissingError
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
|
||||
from odoo.addons.account.controllers.portal import PortalAccount
|
||||
from odoo.addons.hr_timesheet.controllers.portal import TimesheetCustomerPortal
|
||||
from odoo.addons.portal.controllers.portal import pager as portal_pager
|
||||
from odoo.addons.project.controllers.portal import ProjectCustomerPortal
|
||||
|
||||
|
||||
class PortalProjectAccount(PortalAccount, ProjectCustomerPortal):
|
||||
|
||||
def _invoice_get_page_view_values(self, invoice, access_token, **kwargs):
|
||||
values = super()._invoice_get_page_view_values(invoice, access_token, **kwargs)
|
||||
domain = request.env['account.analytic.line']._timesheet_get_portal_domain()
|
||||
domain = expression.AND([
|
||||
domain,
|
||||
request.env['account.analytic.line']._timesheet_get_sale_domain(
|
||||
invoice.mapped('line_ids.sale_line_ids'),
|
||||
request.env['account.move'].browse([invoice.id])
|
||||
)
|
||||
])
|
||||
values['timesheets'] = request.env['account.analytic.line'].sudo().search(domain)
|
||||
values['is_uom_day'] = request.env['account.analytic.line'].sudo()._is_timesheet_encode_uom_day()
|
||||
return values
|
||||
|
||||
@http.route([
|
||||
'/my/tasks/<task_id>/orders/invoices',
|
||||
'/my/tasks/<task_id>/orders/invoices/page/<int:page>'],
|
||||
type='http', auth="user", website=True)
|
||||
def portal_my_tasks_invoices(self, task_id=None, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw):
|
||||
task = request.env['project.task'].search([('id', '=', task_id)])
|
||||
if not task:
|
||||
return NotFound()
|
||||
|
||||
domain = [('id', 'in', task.sale_order_id.invoice_ids.ids)]
|
||||
values = self._prepare_my_invoices_values(page, date_begin, date_end, sortby, filterby, domain=domain)
|
||||
|
||||
# pager
|
||||
pager = portal_pager(**values['pager'])
|
||||
|
||||
# content according to pager and archive selected
|
||||
invoices = values['invoices'](pager['offset'])
|
||||
request.session['my_invoices_history'] = invoices.ids[:100]
|
||||
|
||||
values.update({
|
||||
'invoices': invoices,
|
||||
'pager': pager,
|
||||
})
|
||||
|
||||
return request.render("account.portal_my_invoices", values)
|
||||
|
||||
|
||||
class SaleTimesheetCustomerPortal(TimesheetCustomerPortal):
|
||||
|
||||
def _get_searchbar_inputs(self):
|
||||
searchbar_inputs = super()._get_searchbar_inputs()
|
||||
searchbar_inputs.update(
|
||||
so={'input': 'so', 'label': _('Search in Sales Order')},
|
||||
sol={'input': 'sol', 'label': _('Search in Sales Order Item')},
|
||||
invoice={'input': 'invoice', 'label': _('Search in Invoice')})
|
||||
return searchbar_inputs
|
||||
|
||||
def _get_searchbar_groupby(self):
|
||||
searchbar_groupby = super()._get_searchbar_groupby()
|
||||
searchbar_groupby.update(
|
||||
so={'input': 'so', 'label': _('Sales Order')},
|
||||
sol={'input': 'sol', 'label': _('Sales Order Item')},
|
||||
invoice={'input': 'invoice', 'label': _('Invoice')})
|
||||
return searchbar_groupby
|
||||
|
||||
def _get_search_domain(self, search_in, search):
|
||||
search_domain = super()._get_search_domain(search_in, search)
|
||||
if search_in in ('sol', 'all'):
|
||||
search_domain = expression.OR([search_domain, [('so_line', 'ilike', search)]])
|
||||
if search_in in ('so', 'all'):
|
||||
search_domain = expression.OR([search_domain, [('so_line.order_id.name', 'ilike', search)]])
|
||||
if search_in in ('invoice', 'all'):
|
||||
invoices = request.env['account.move'].sudo().search([('name', 'ilike', search)])
|
||||
domain = request.env['account.analytic.line']._timesheet_get_sale_domain(invoices.mapped('invoice_line_ids.sale_line_ids'), invoices)
|
||||
search_domain = expression.OR([search_domain, domain])
|
||||
return search_domain
|
||||
|
||||
def _get_groupby_mapping(self):
|
||||
groupby_mapping = super()._get_groupby_mapping()
|
||||
groupby_mapping.update(
|
||||
sol='so_line',
|
||||
so='order_id',
|
||||
invoice='timesheet_invoice_id')
|
||||
return groupby_mapping
|
||||
|
||||
def _get_searchbar_sortings(self):
|
||||
searchbar_sortings = super()._get_searchbar_sortings()
|
||||
searchbar_sortings.update(
|
||||
sol={'label': _('Sales Order Item'), 'order': 'so_line'})
|
||||
return searchbar_sortings
|
||||
|
||||
def _task_get_page_view_values(self, task, access_token, **kwargs):
|
||||
values = super()._task_get_page_view_values(task, access_token, **kwargs)
|
||||
values['so_accessible'] = False
|
||||
try:
|
||||
if task.sale_order_id and self._document_check_access('sale.order', task.sale_order_id.id):
|
||||
values['so_accessible'] = True
|
||||
title = _('Quotation') if task.sale_order_id.state in ['draft', 'sent'] else _('Sales Order')
|
||||
values['task_link_section'].append({
|
||||
'access_url': task.sale_order_id.get_portal_url(),
|
||||
'title': title,
|
||||
})
|
||||
except (AccessError, MissingError):
|
||||
pass
|
||||
|
||||
moves = request.env['account.move']
|
||||
invoice_ids = task.sale_order_id.invoice_ids
|
||||
if invoice_ids and request.env['account.move'].check_access_rights('read', raise_exception=False):
|
||||
moves = request.env['account.move'].search([('id', 'in', invoice_ids.ids)])
|
||||
values['invoices_accessible'] = moves.ids
|
||||
if moves:
|
||||
if len(moves) == 1:
|
||||
task_invoice_url = moves.get_portal_url()
|
||||
title = _('Invoice')
|
||||
else:
|
||||
task_invoice_url = f'/my/tasks/{task.id}/orders/invoices'
|
||||
title = _('Invoices')
|
||||
values['task_link_section'].append({
|
||||
'access_url': task_invoice_url,
|
||||
'title': title,
|
||||
})
|
||||
return values
|
||||
|
||||
@http.route(['/my/timesheets', '/my/timesheets/page/<int:page>'], type='http', auth="user", website=True)
|
||||
def portal_my_timesheets(self, page=1, sortby=None, filterby=None, search=None, search_in='all', groupby='sol', **kw):
|
||||
return super().portal_my_timesheets(page, sortby, filterby, search, search_in, groupby, **kw)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="time_product" model="product.product">
|
||||
<field name="name">Service on Timesheet</field>
|
||||
<field name="type">service</field>
|
||||
<field name="list_price">40</field>
|
||||
<field name="uom_id" ref="uom.product_uom_hour"/>
|
||||
<field name="uom_po_id" ref="uom.product_uom_hour"/>
|
||||
<field name="service_policy">delivered_timesheet</field>
|
||||
<field name="image_1920" type="base64" file="sale_timesheet/static/img/product_product_time_product.png"/>
|
||||
</record>
|
||||
</data>
|
||||
<data>
|
||||
<record model="res.groups" id="base.group_user">
|
||||
<field name="implied_ids" eval="[(4, ref('uom.group_uom'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
File diff suppressed because it is too large
Load diff
1587
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/af.po
Normal file
1587
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/af.po
Normal file
File diff suppressed because it is too large
Load diff
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/am.po
Normal file
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/am.po
Normal file
File diff suppressed because it is too large
Load diff
1696
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ar.po
Normal file
1696
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ar.po
Normal file
File diff suppressed because it is too large
Load diff
1607
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/az.po
Normal file
1607
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/az.po
Normal file
File diff suppressed because it is too large
Load diff
1599
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/be.po
Normal file
1599
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/be.po
Normal file
File diff suppressed because it is too large
Load diff
1626
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/bg.po
Normal file
1626
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/bg.po
Normal file
File diff suppressed because it is too large
Load diff
1595
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/bs.po
Normal file
1595
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/bs.po
Normal file
File diff suppressed because it is too large
Load diff
1724
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ca.po
Normal file
1724
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ca.po
Normal file
File diff suppressed because it is too large
Load diff
1702
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/cs.po
Normal file
1702
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/cs.po
Normal file
File diff suppressed because it is too large
Load diff
1669
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/da.po
Normal file
1669
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/da.po
Normal file
File diff suppressed because it is too large
Load diff
1731
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/de.po
Normal file
1731
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/de.po
Normal file
File diff suppressed because it is too large
Load diff
1295
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/el.po
Normal file
1295
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/el.po
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,102 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 15:21+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: English (Australia) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/en_AU/)\n"
|
||||
"Language: en_AU\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Employee"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Sales Order"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Sales Order Line"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid "Timesheet UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
msgid "account analytic line"
|
||||
msgstr ""
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
# Andi Chandler <andi@gowling.com>, 2016
|
||||
# James Dove <james@oceancave.com>, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2016-04-21 09:27+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language-Team: English (United Kingdom) (http://www.transifex.com/odoo/"
|
||||
"odoo-9/language/en_GB/)\n"
|
||||
"Language: en_GB\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Companies"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Employee"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Product Template"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
#, fuzzy
|
||||
msgid "Project associated to this sale"
|
||||
msgstr "Timesheet activities associated to this sale"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Sales Order"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Sales Order Line"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr "Timesheet Cost"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Timesheet"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr "Timesheet activities"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr "Timesheet activities associated to this sale"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Timesheets"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Analytic Line"
|
||||
1721
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/es.po
Normal file
1721
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/es.po
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,103 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-12-11 15:41+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Spanish (Bolivia) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/es_BO/)\n"
|
||||
"Language: es_BO\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla de producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Pedido de Venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid "Timesheet UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea analítica"
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-10-06 08:55+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Spanish (Chile) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/es_CL/)\n"
|
||||
"Language: es_CL\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empleado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Línea de pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid "Timesheet UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea Analítica"
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
# #-#-#-#-# es_CO.po (Odoo 9.0) #-#-#-#-#
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
# Mateo Tibaquirá <nestormateo@gmail.com>, 2015
|
||||
# #-#-#-#-# es_CO.po (Odoo 9.0) #-#-#-#-#
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-10-23 21:08+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Spanish (Colombia) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/es_CO/)\n"
|
||||
"Language: es_CO\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"#-#-#-#-# es_CO.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"#-#-#-#-# es_CO.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empleado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla del Producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
#, fuzzy
|
||||
msgid "Project associated to this sale"
|
||||
msgstr "Actividades del parte de horas asociadas a esta venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
#, fuzzy
|
||||
msgid "Sales Order"
|
||||
msgstr ""
|
||||
"#-#-#-#-# es_CO.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Orden de Venta\n"
|
||||
"#-#-#-#-# es_CO.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Pedido de Venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
#, fuzzy
|
||||
msgid "Sales Order Line"
|
||||
msgstr ""
|
||||
"#-#-#-#-# es_CO.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Línea Orden de Venta\n"
|
||||
"#-#-#-#-# es_CO.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Línea de Pedido de Venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr "Costo del Parte de Horas"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Parte de Horas"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr "Actividades del parte de horas"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr "Actividades del parte de horas asociadas a esta venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Partes de Horas"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
"Puede usar sólo un producto en el parte de horas dentro de la misma orden de "
|
||||
"venta. Debería dividir su orden para incluir sólo un contrato basado en "
|
||||
"tiempos y materiales."
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea Analítica"
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 15:21+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Spanish (Costa Rica) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/es_CR/)\n"
|
||||
"Language: es_CR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empleado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla de producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Línea pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Hojas de trabajo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Hojas de trabajo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea Analítica"
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
# Juliano Henriquez <juliano@consultoriahenca.com>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2016-05-19 05:27+0000\n"
|
||||
"Last-Translator: Juliano Henriquez <juliano@consultoriahenca.com>\n"
|
||||
"Language-Team: Spanish (Dominican Republic) (http://www.transifex.com/odoo/"
|
||||
"odoo-9/language/es_DO/)\n"
|
||||
"Language: es_DO\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empleado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
#, fuzzy
|
||||
msgid "Project associated to this sale"
|
||||
msgstr "Actividades de Hoja de Tiempos relacionadas con esta venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Aviso para pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Línea pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr "Costos de Hoja de Tiempo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Hoja de Tiempo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr "Actividades de Hoja de Tiempo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr "Actividades de Hoja de Tiempos relacionadas con esta venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Partes de tiempo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
"Se puede utilizar un solo producto en hoja de tiempo de la misma orden de "
|
||||
"venta. Usted debe dividir el fin de incluir sólo un contrato basado en "
|
||||
"tiempo y material."
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea analítica"
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
# Rick Hunter <rick_hunter_ec@yahoo.com>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2016-02-04 18:16+0000\n"
|
||||
"Last-Translator: Rick Hunter <rick_hunter_ec@yahoo.com>\n"
|
||||
"Language-Team: Spanish (Ecuador) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/es_EC/)\n"
|
||||
"Language: es_EC\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empleado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
#, fuzzy
|
||||
msgid "Project associated to this sale"
|
||||
msgstr "Actividades de las Hojas de Registro asociadas a esta venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Aviso para pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Línea pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr "Costo de la Hoja de Registro"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Hoja de registros"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr "Actividades de la Hoja de Registro"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr "Actividades de las Hojas de Registro asociadas a esta venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Hojas de registro"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
"Solo puede usar un único producto en la Hoja de Registro dentro del mismo "
|
||||
"pedido de venta. Debe dividir el pedido para incluir un único contrato "
|
||||
"basado en tiempo y materiales."
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea Analítica"
|
||||
1717
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/es_MX.po
Normal file
1717
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/es_MX.po
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,102 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 15:21+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Spanish (Panama) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/es_PA/)\n"
|
||||
"Language: es_PA\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla de producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid "Timesheet UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
msgid "account analytic line"
|
||||
msgstr ""
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
# Carlos Eduardo Rodriguez Rossi <crodriguez@samemotion.com>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2016-06-16 20:05+0000\n"
|
||||
"Last-Translator: Carlos Eduardo Rodriguez Rossi <crodriguez@samemotion.com>\n"
|
||||
"Language-Team: Spanish (Peru) (http://www.transifex.com/odoo/odoo-9/language/"
|
||||
"es_PE/)\n"
|
||||
"Language: es_PE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañias"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empleado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla de Producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
#, fuzzy
|
||||
msgid "Project associated to this sale"
|
||||
msgstr "Actividades de Hoja de Tiempo asociadas a esta venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Pedidos de Venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Línea de Pedido de Venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr "Costo de Hoja de Tiempo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Hoja de Tiempo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr "Actividades de Hoja de Tiempo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr "Actividades de Hoja de Tiempo asociadas a esta venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Hoja de Tiempo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
"Solamente puede usar un producto por hoja de tiempo dentro del mismo pedido "
|
||||
"de venta. Debería dividir su pedido para incluir solamente un contrato "
|
||||
"basado en tiempo y material."
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea Analítica"
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 15:21+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Spanish (Paraguay) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/es_PY/)\n"
|
||||
"Language: es_PY\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empleado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla de producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Línea pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid "Timesheet UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea analítica"
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 15:21+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Spanish (Venezuela) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/es_VE/)\n"
|
||||
"Language: es_VE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empleado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Plantilla de producto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Línea pedido de venta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Hojas de trabajo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Hojas de trabajo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Línea analítica"
|
||||
1717
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/et.po
Normal file
1717
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/et.po
Normal file
File diff suppressed because it is too large
Load diff
102
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/eu.po
Normal file
102
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/eu.po
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2016-02-02 19:56+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Basque (http://www.transifex.com/odoo/odoo-9/language/eu/)\n"
|
||||
"Language: eu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Enpresak"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Product Template"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Salmenta eskaria"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Salmenta lerroa"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid "Timesheet UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Lerro analitikoa"
|
||||
1637
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fa.po
Normal file
1637
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fa.po
Normal file
File diff suppressed because it is too large
Load diff
1726
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fi.po
Normal file
1726
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fi.po
Normal file
File diff suppressed because it is too large
Load diff
101
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fo.po
Normal file
101
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fo.po
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-12-22 23:50+0000\n"
|
||||
"Last-Translator: Jarnhold Nattestad <nattestads@gmail.com>\n"
|
||||
"Language-Team: Faroese (http://www.transifex.com/odoo/odoo-9/language/fo/)\n"
|
||||
"Language: fo\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Fyritøkur"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Søluordri"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Søluordra-linja"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid "Timesheet UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
msgid "account analytic line"
|
||||
msgstr ""
|
||||
1719
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fr.po
Normal file
1719
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fr.po
Normal file
File diff suppressed because it is too large
Load diff
1537
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fr_BE.po
Normal file
1537
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/fr_BE.po
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,102 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-12-27 10:27+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: French (Canada) (http://www.transifex.com/odoo/odoo-9/"
|
||||
"language/fr_CA/)\n"
|
||||
"Language: fr_CA\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Sociétés"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Modèle de produit"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Bon de vente"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid "Timesheet UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
msgid "account analytic line"
|
||||
msgstr ""
|
||||
103
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/gl.po
Normal file
103
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/gl.po
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 15:21+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Galician (http://www.transifex.com/odoo/odoo-9/language/gl/)\n"
|
||||
"Language: gl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Empregado"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Modelo de produto"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Pedido de venda"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Liña de ordes de venda"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Follas de traballo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Follas de traballo"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Liña analítica"
|
||||
1587
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/gu.po
Normal file
1587
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/gu.po
Normal file
File diff suppressed because it is too large
Load diff
1668
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/he.po
Normal file
1668
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/he.po
Normal file
File diff suppressed because it is too large
Load diff
1603
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/hi.po
Normal file
1603
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/hi.po
Normal file
File diff suppressed because it is too large
Load diff
1631
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/hr.po
Normal file
1631
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/hr.po
Normal file
File diff suppressed because it is too large
Load diff
1643
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/hu.po
Normal file
1643
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/hu.po
Normal file
File diff suppressed because it is too large
Load diff
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/hy.po
Normal file
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/hy.po
Normal file
File diff suppressed because it is too large
Load diff
1706
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/id.po
Normal file
1706
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/id.po
Normal file
File diff suppressed because it is too large
Load diff
1589
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/is.po
Normal file
1589
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/is.po
Normal file
File diff suppressed because it is too large
Load diff
1713
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/it.po
Normal file
1713
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/it.po
Normal file
File diff suppressed because it is too large
Load diff
1655
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ja.po
Normal file
1655
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ja.po
Normal file
File diff suppressed because it is too large
Load diff
106
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ka.po
Normal file
106
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ka.po
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 15:21+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Georgian (http://www.transifex.com/odoo/odoo-9/language/ka/)\n"
|
||||
"Language: ka\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"#-#-#-#-# ka.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"#-#-#-#-# ka.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "კომპანიები"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "თანამშრომელი"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "გაყიდვის ორდერი"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "გაყიდვის ორდერის ხაზი"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "დროის აღრიცხვა"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "დროის აღრიცხვა"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
msgid "account analytic line"
|
||||
msgstr ""
|
||||
107
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/kab.po
Normal file
107
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/kab.po
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 15:21+0000\n"
|
||||
"Last-Translator: Martin Trigaux\n"
|
||||
"Language-Team: Kabyle (http://www.transifex.com/odoo/odoo-9/language/kab/)\n"
|
||||
"Language: kab\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"#-#-#-#-# kab.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"#-#-#-#-# kab.po (Odoo 9.0) #-#-#-#-#\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Tikebbaniyin"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Amaris"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Taneɣruft n ufaris"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Taladna n uznuzu"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Izirig n tladna n uznuzu"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Tiferkit n wakud"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Tiferka n wakud"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Izirig usliḍ"
|
||||
1640
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/km.po
Normal file
1640
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/km.po
Normal file
File diff suppressed because it is too large
Load diff
1663
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ko.po
Normal file
1663
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ko.po
Normal file
File diff suppressed because it is too large
Load diff
1251
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/lb.po
Normal file
1251
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/lb.po
Normal file
File diff suppressed because it is too large
Load diff
1590
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/lo.po
Normal file
1590
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/lo.po
Normal file
File diff suppressed because it is too large
Load diff
1641
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/lt.po
Normal file
1641
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/lt.po
Normal file
File diff suppressed because it is too large
Load diff
1612
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/lv.po
Normal file
1612
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/lv.po
Normal file
File diff suppressed because it is too large
Load diff
105
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/mk.po
Normal file
105
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/mk.po
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
# Aleksandar Vangelovski <aleksandarv@hbee.eu>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-18 14:08+0000\n"
|
||||
"PO-Revision-Date: 2016-05-12 12:36+0000\n"
|
||||
"Last-Translator: Aleksandar Vangelovski <aleksandarv@hbee.eu>\n"
|
||||
"Language-Team: Macedonian (http://www.transifex.com/odoo/odoo-9/language/"
|
||||
"mk/)\n"
|
||||
"Language: mk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Компании"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Вработен"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Урнек на производ"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Налог за продажба"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Ставка од налог за продажба"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
msgid ""
|
||||
"This will set the unit of measure used in projects and tasks.\n"
|
||||
"If you use the timesheet linked to projects, don't forget to setup the right "
|
||||
"unit of measure in your employees."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr "Трошок на временска таблица"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.project_time_mode_id_duplicate_xmlid
|
||||
#, fuzzy
|
||||
msgid "Timesheet UoM"
|
||||
msgstr "Временска таблица"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Timesheets"
|
||||
msgstr "Временски таблици"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_timesheet.py:128
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can use only one product on timesheet within the same sale order. You "
|
||||
"should split your order to include only one contract based on time and "
|
||||
"material."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
#, fuzzy
|
||||
msgid "account analytic line"
|
||||
msgstr "Аналитичка ставка"
|
||||
1610
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ml.po
Normal file
1610
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ml.po
Normal file
File diff suppressed because it is too large
Load diff
1624
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/mn.po
Normal file
1624
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/mn.po
Normal file
File diff suppressed because it is too large
Load diff
1602
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ms.po
Normal file
1602
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ms.po
Normal file
File diff suppressed because it is too large
Load diff
1617
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/nb.po
Normal file
1617
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/nb.po
Normal file
File diff suppressed because it is too large
Load diff
1721
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/nl.po
Normal file
1721
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/nl.po
Normal file
File diff suppressed because it is too large
Load diff
1588
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/no.po
Normal file
1588
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/no.po
Normal file
File diff suppressed because it is too large
Load diff
1727
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/pl.po
Normal file
1727
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/pl.po
Normal file
File diff suppressed because it is too large
Load diff
1632
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/pt.po
Normal file
1632
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/pt.po
Normal file
File diff suppressed because it is too large
Load diff
1718
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/pt_BR.po
Normal file
1718
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/pt_BR.po
Normal file
File diff suppressed because it is too large
Load diff
1709
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ro.po
Normal file
1709
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ro.po
Normal file
File diff suppressed because it is too large
Load diff
1721
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ru.po
Normal file
1721
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ru.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1670
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sk.po
Normal file
1670
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sk.po
Normal file
File diff suppressed because it is too large
Load diff
1625
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sl.po
Normal file
1625
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sl.po
Normal file
File diff suppressed because it is too large
Load diff
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sq.po
Normal file
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sq.po
Normal file
File diff suppressed because it is too large
Load diff
1700
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sr.po
Normal file
1700
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sr.po
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,492 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_timesheet
|
||||
#
|
||||
# Translators:
|
||||
# Martin Trigaux <mat@odoo.com>, 2017
|
||||
# Djordje Marjanovic <djordje_m@yahoo.com>, 2017
|
||||
# Nemanja Dragovic <nemanjadragovic94@gmail.com>, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 10.saas~18\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-10-02 11:26+0000\n"
|
||||
"PO-Revision-Date: 2017-10-02 11:26+0000\n"
|
||||
"Last-Translator: Nemanja Dragovic <nemanjadragovic94@gmail.com>, 2017\n"
|
||||
"Language-Team: Serbian (Latin) (https://www.transifex.com/odoo/teams/41243/sr%40latin/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Language: sr@latin\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/account_invoice.py:32
|
||||
#: code:addons/sale_timesheet/models/project.py:26
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" <p class=\"oe_view_nocontent_create\">\n"
|
||||
" Click to record timesheets.\n"
|
||||
" </p><p>\n"
|
||||
" You can register and track your workings hours by project every\n"
|
||||
" day. Every time spent on a project will become a cost and can be re-invoiced to\n"
|
||||
" customers if required.\n"
|
||||
" </p>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "<b>Total</b>"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.project_project_view_kanban_inherit_sale_timesheet
|
||||
msgid "<span class=\"o_label\">Overview</span>"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_analytic_line
|
||||
msgid "Analytic Line"
|
||||
msgstr "Analiticki red"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:account.analytic.line,timesheet_invoice_type:0
|
||||
msgid "Billable Fixed"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_view_search
|
||||
msgid "Billable Hours"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:account.analytic.line,timesheet_invoice_type:0
|
||||
msgid "Billable Time"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_account_analytic_line_timesheet_invoice_type
|
||||
msgid "Billable Type"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Billable fixed"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Billable time"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.filters,name:sale_timesheet.timesheet_filter_billing
|
||||
msgid "Billing Rate"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.actions.act_window,name:sale_timesheet.timesheet_action_billing_report
|
||||
#: model:ir.ui.menu,name:sale_timesheet.menu_timesheet_billing_analysis
|
||||
msgid "By Billing Rate"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Cost"
|
||||
msgstr "Cijena koštanja"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.actions.act_window,name:sale_timesheet.timesheet_action_report_cost_revenue
|
||||
#: model:ir.ui.menu,name:sale_timesheet.menu_timesheet_report_cost_revenue
|
||||
msgid "Costs and Revenues"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:product.template,service_tracking:0
|
||||
msgid "Create a new project but no task"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:product.template,service_tracking:0
|
||||
msgid "Create a task in a new project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:product.template,service_tracking:0
|
||||
msgid "Create a task in an existing project"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_currency_id
|
||||
msgid "Currency"
|
||||
msgstr "Valuta"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:product.template,service_tracking:0
|
||||
msgid "Don't create task"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_hr_employee
|
||||
msgid "Employee"
|
||||
msgstr "Zaposleni"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Fixed"
|
||||
msgstr "Fiksno"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_view_search
|
||||
msgid "Fixed Price Projects"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.product_template_search_view_sale_timesheet
|
||||
msgid "Fixed price services"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Hours"
|
||||
msgstr "Sati"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_account_invoice
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_account_analytic_line_timesheet_invoice_id
|
||||
msgid "Invoice"
|
||||
msgstr "Faktura"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_product_product_service_policy
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_product_template_service_policy
|
||||
msgid "Invoice based on"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_account_analytic_line_timesheet_invoice_id
|
||||
msgid "Invoice created from the timesheet"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Invoiced"
|
||||
msgstr "Fakturisano"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_line_is_service
|
||||
msgid "Is a Service"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.product_template_search_view_sale_timesheet
|
||||
msgid "Milestone services"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:product.template,service_policy:0
|
||||
msgid "Milestones (manually set quantities on order)"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:account.analytic.line,timesheet_invoice_type:0
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "No task found"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:account.analytic.line,timesheet_invoice_type:0
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Non Billable"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_view_search
|
||||
msgid "Non Billable Hours"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Non billable"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_account_invoice_timesheet_count
|
||||
msgid "Number of timesheets"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_product_product_service_tracking
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_product_template_service_tracking
|
||||
msgid ""
|
||||
"On Sales order confirmation, this product can generate a project and/or "
|
||||
"task. From those, you can track the service you are selling."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:product.template,service_policy:0
|
||||
msgid "Ordered quantities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/project.py:45
|
||||
#, python-format
|
||||
msgid "Overview"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_product_template
|
||||
msgid "Product Template"
|
||||
msgstr "Predložak proizvoda"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.actions.act_window,name:sale_timesheet.product_template_action_fixed
|
||||
#: model:ir.actions.act_window,name:sale_timesheet.product_template_action_milestone
|
||||
#: model:ir.actions.act_window,name:sale_timesheet.product_template_action_time_based
|
||||
msgid "Products"
|
||||
msgstr "Proizvodi"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Profitability"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_project_project
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_product_product_project_id
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_product_template_project_id
|
||||
msgid "Project"
|
||||
msgstr "Projekat"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_project_id
|
||||
msgid "Project associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
msgid "Project(s) Overview"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_order.py:107
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_project_ids
|
||||
#, python-format
|
||||
msgid "Projects"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_sale_order_project_ids
|
||||
msgid "Projects used in this sales order."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order
|
||||
msgid "Quotation"
|
||||
msgstr "Ponuda"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Rates"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_account_analytic_line_timesheet_revenue
|
||||
msgid "Revenue"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_project_project_sale_line_id
|
||||
msgid ""
|
||||
"Sale order line from which the project has been created. Used for "
|
||||
"tracability."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_sale_service_inherit_form2
|
||||
msgid "Sales Order"
|
||||
msgstr "Prodajni Nalog"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_project_task_sale_line_id
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_sale_service_inherit_form2
|
||||
msgid "Sales Order Item"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_sale_order_line
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_project_project_sale_line_id
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Stavka naloga za prodaju"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_sale_order_line_is_service
|
||||
msgid ""
|
||||
"Sales Order item should generate a task and/or a project, depending on the "
|
||||
"product settings."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_product_product_project_id
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_product_template_project_id
|
||||
msgid ""
|
||||
"Select a non billable project on which tasks can be created. This setting "
|
||||
"must be set for each company."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_product_product_service_tracking
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_product_template_service_tracking
|
||||
msgid "Service Tracking"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.res_config_settings_view_form
|
||||
msgid "Setup your fixed price services"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.res_config_settings_view_form
|
||||
msgid "Setup your milestone services"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.res_config_settings_view_form
|
||||
msgid "Setup your time-based services"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model,name:sale_timesheet.model_project_task
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_line_task_id
|
||||
msgid "Task"
|
||||
msgstr "Zadatak"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_order.py:234
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Task Created (%s): <a href=# data-oe-model=project.task data-oe-id=%d>%s</a>"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,help:sale_timesheet.field_sale_order_line_task_id
|
||||
msgid "Task generated by the sales order item"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/controllers/main.py:109
|
||||
#: code:addons/sale_timesheet/controllers/main.py:141
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_tasks_count
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
#, python-format
|
||||
msgid "Tasks"
|
||||
msgstr "Zadaci"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_tasks_ids
|
||||
msgid "Tasks associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "There is no timesheet for now."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/sale_order.py:237
|
||||
#, python-format
|
||||
msgid ""
|
||||
"This task has been created from: <a href=# data-oe-model=sale.order data-oe-"
|
||||
"id=%d>%s</a> (%s)"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Time and material"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "Time by people"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.product_template_search_view_sale_timesheet
|
||||
msgid "Time-based services"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#. openerp-web
|
||||
#: code:addons/sale_timesheet/static/src/js/timesheet_plan.js:143
|
||||
#: model:ir.actions.act_window,name:sale_timesheet.action_timesheet_from_invoice
|
||||
#: model:ir.actions.act_window,name:sale_timesheet.timesheet_action_from_plan
|
||||
#: model:ir.actions.act_window,name:sale_timesheet.timesheet_action_plan_pivot
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_view_pivot_revenue
|
||||
#, python-format
|
||||
msgid "Timesheet"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_hr_employee_timesheet_cost
|
||||
msgid "Timesheet Cost"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_count
|
||||
msgid "Timesheet activities"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_sale_order_timesheet_ids
|
||||
msgid "Timesheet activities associated to this sale"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/controllers/main.py:101
|
||||
#: code:addons/sale_timesheet/controllers/main.py:130
|
||||
#: code:addons/sale_timesheet/models/account_invoice.py:26
|
||||
#: model:ir.model.fields,field_description:sale_timesheet.field_account_invoice_timesheet_ids
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.account_invoice_view_form_inherit_sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.view_order_form_inherit_sale_timesheet
|
||||
#, python-format
|
||||
msgid "Timesheets"
|
||||
msgstr "Karneti ( vremena rada)"
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/project.py:20
|
||||
#, python-format
|
||||
msgid "Timesheets of %s"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: selection:product.template,service_policy:0
|
||||
msgid "Timesheets on tasks"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.timesheet_plan
|
||||
msgid "To invoice"
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/account.py:32
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can not modify already invoiced timesheets (linked to a Sales order "
|
||||
"items invoiced on Time and material)."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: code:addons/sale_timesheet/models/project.py:74
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot delete a task related to a Sales Order. You can only archive this"
|
||||
" task."
|
||||
msgstr ""
|
||||
|
||||
#. module: sale_timesheet
|
||||
#: model_terms:ir.ui.view,arch_db:sale_timesheet.hr_timesheet_employee_extd_form
|
||||
msgid "per hour"
|
||||
msgstr ""
|
||||
1696
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sv.po
Normal file
1696
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sv.po
Normal file
File diff suppressed because it is too large
Load diff
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sw.po
Normal file
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/sw.po
Normal file
File diff suppressed because it is too large
Load diff
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ta.po
Normal file
1569
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/ta.po
Normal file
File diff suppressed because it is too large
Load diff
1696
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/th.po
Normal file
1696
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/th.po
Normal file
File diff suppressed because it is too large
Load diff
1714
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/tr.po
Normal file
1714
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/tr.po
Normal file
File diff suppressed because it is too large
Load diff
1713
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/uk.po
Normal file
1713
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/uk.po
Normal file
File diff suppressed because it is too large
Load diff
1711
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/vi.po
Normal file
1711
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/vi.po
Normal file
File diff suppressed because it is too large
Load diff
1654
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/zh_CN.po
Normal file
1654
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/zh_CN.po
Normal file
File diff suppressed because it is too large
Load diff
1655
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/zh_TW.po
Normal file
1655
odoo-bringout-oca-ocb-sale_timesheet/sale_timesheet/i18n/zh_TW.po
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account
|
||||
from . import account_move
|
||||
from . import product
|
||||
from . import project
|
||||
from . import project_update
|
||||
from . import sale_order
|
||||
from . import res_config_settings
|
||||
from . import project_sale_line_employee_map
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.osv import expression
|
||||
from odoo.tools.misc import unquote
|
||||
|
||||
TIMESHEET_INVOICE_TYPES = [
|
||||
('billable_time', 'Billed on Timesheets'),
|
||||
('billable_fixed', 'Billed at a Fixed price'),
|
||||
('billable_milestones', 'Billed on Milestones'),
|
||||
('billable_manual', 'Billed Manually'),
|
||||
('non_billable', 'Non Billable Tasks'),
|
||||
('timesheet_revenues', 'Timesheet Revenues'),
|
||||
('service_revenues', 'Service Revenues'),
|
||||
('other_revenues', 'Other revenues'),
|
||||
('other_costs', 'Other costs'),
|
||||
]
|
||||
|
||||
class AccountAnalyticLine(models.Model):
|
||||
_inherit = 'account.analytic.line'
|
||||
|
||||
def _default_sale_line_domain(self):
|
||||
domain = super(AccountAnalyticLine, self)._default_sale_line_domain()
|
||||
return expression.OR([domain, [('qty_delivered_method', '=', 'timesheet')]])
|
||||
|
||||
def _get_sellable_line_domain(self, domain):
|
||||
return self.env['project.task']._get_sellable_line_domain(domain)
|
||||
|
||||
def _domain_so_line(self):
|
||||
domain = expression.AND([
|
||||
self.env['sale.order.line']._sellable_lines_domain(),
|
||||
[
|
||||
('is_service', '=', True),
|
||||
('is_expense', '=', False),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('order_partner_id', 'child_of', unquote('commercial_partner_id')),
|
||||
],
|
||||
])
|
||||
return str(domain)
|
||||
|
||||
timesheet_invoice_type = fields.Selection(TIMESHEET_INVOICE_TYPES, string="Billable Type",
|
||||
compute='_compute_timesheet_invoice_type', compute_sudo=True, store=True, readonly=True)
|
||||
commercial_partner_id = fields.Many2one('res.partner', compute="_compute_commercial_partner")
|
||||
timesheet_invoice_id = fields.Many2one('account.move', string="Invoice", readonly=True, copy=False, help="Invoice created from the timesheet", index='btree_not_null')
|
||||
so_line = fields.Many2one(compute="_compute_so_line", store=True, readonly=False,
|
||||
domain=_domain_so_line,
|
||||
help="Sales order item to which the time spent will be added in order to be invoiced to your customer. Remove the sales order item for the timesheet entry to be non-billable.")
|
||||
# we needed to store it only in order to be able to groupby in the portal
|
||||
order_id = fields.Many2one(related='so_line.order_id', store=True, readonly=True, index=True)
|
||||
is_so_line_edited = fields.Boolean("Is Sales Order Item Manually Edited")
|
||||
|
||||
@api.depends('project_id.commercial_partner_id', 'task_id.commercial_partner_id')
|
||||
def _compute_commercial_partner(self):
|
||||
for timesheet in self:
|
||||
timesheet.commercial_partner_id = timesheet.task_id.commercial_partner_id or timesheet.project_id.commercial_partner_id
|
||||
|
||||
@api.depends('so_line.product_id', 'project_id', 'amount')
|
||||
def _compute_timesheet_invoice_type(self):
|
||||
for timesheet in self:
|
||||
if timesheet.project_id: # AAL will be set to False
|
||||
invoice_type = 'non_billable' if not timesheet.so_line else False
|
||||
if timesheet.so_line and timesheet.so_line.product_id.type == 'service':
|
||||
if timesheet.so_line.product_id.invoice_policy == 'delivery':
|
||||
if timesheet.so_line.product_id.service_type == 'timesheet':
|
||||
invoice_type = 'timesheet_revenues' if timesheet.amount > 0 else 'billable_time'
|
||||
else:
|
||||
service_type = timesheet.so_line.product_id.service_type
|
||||
invoice_type = f'billable_{service_type}' if service_type in ['milestones', 'manual'] else 'billable_fixed'
|
||||
elif timesheet.so_line.product_id.invoice_policy == 'order':
|
||||
invoice_type = 'billable_fixed'
|
||||
timesheet.timesheet_invoice_type = invoice_type
|
||||
else:
|
||||
if timesheet.amount >= 0:
|
||||
if timesheet.so_line and timesheet.so_line.product_id.type == 'service':
|
||||
timesheet.timesheet_invoice_type = 'service_revenues'
|
||||
else:
|
||||
timesheet.timesheet_invoice_type = 'other_revenues'
|
||||
else:
|
||||
timesheet.timesheet_invoice_type = 'other_costs'
|
||||
|
||||
@api.depends('task_id.sale_line_id', 'project_id.sale_line_id', 'employee_id', 'project_id.allow_billable')
|
||||
def _compute_so_line(self):
|
||||
for timesheet in self.filtered(lambda t: not t.is_so_line_edited and t._is_not_billed()): # Get only the timesheets are not yet invoiced
|
||||
timesheet.so_line = timesheet.project_id.allow_billable and timesheet._timesheet_determine_sale_line()
|
||||
|
||||
@api.depends('timesheet_invoice_id.state')
|
||||
def _compute_partner_id(self):
|
||||
super(AccountAnalyticLine, self.filtered(lambda t: t._is_not_billed()))._compute_partner_id()
|
||||
|
||||
@api.depends('timesheet_invoice_id.state')
|
||||
def _compute_project_id(self):
|
||||
super(AccountAnalyticLine, self.filtered(lambda t: t._is_not_billed()))._compute_project_id()
|
||||
|
||||
def _is_not_billed(self):
|
||||
self.ensure_one()
|
||||
return not self.timesheet_invoice_id or (self.timesheet_invoice_id.state == 'cancel' and self.timesheet_invoice_id.payment_state != 'invoicing_legacy')
|
||||
|
||||
def _check_timesheet_can_be_billed(self):
|
||||
return self.so_line in self.project_id.mapped('sale_line_employee_ids.sale_line_id') | self.task_id.sale_line_id | self.project_id.sale_line_id
|
||||
|
||||
def write(self, values):
|
||||
# prevent to update invoiced timesheets if one line is of type delivery
|
||||
self._check_can_write(values)
|
||||
result = super(AccountAnalyticLine, self).write(values)
|
||||
return result
|
||||
|
||||
def _check_can_write(self, values):
|
||||
if self.sudo().filtered(lambda aal: aal.so_line.product_id.invoice_policy == "delivery") and self.filtered(lambda t: t.timesheet_invoice_id and t.timesheet_invoice_id.state != 'cancel'):
|
||||
if any(field_name in values for field_name in ['unit_amount', 'employee_id', 'project_id', 'task_id', 'so_line', 'date']):
|
||||
raise UserError(_('You cannot modify timesheets that are already invoiced.'))
|
||||
|
||||
def _timesheet_determine_sale_line(self):
|
||||
""" Deduce the SO line associated to the timesheet line:
|
||||
1/ timesheet on task rate: the so line will be the one from the task
|
||||
2/ timesheet on employee rate task: find the SO line in the map of the project (even for subtask), or fallback on the SO line of the task, or fallback
|
||||
on the one on the project
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
if not self.task_id:
|
||||
if self.project_id.pricing_type == 'employee_rate':
|
||||
map_entry = self._get_employee_mapping_entry()
|
||||
if map_entry:
|
||||
return map_entry.sale_line_id
|
||||
if self.project_id.sale_line_id:
|
||||
return self.project_id.sale_line_id
|
||||
if self.task_id.allow_billable and self.task_id.sale_line_id:
|
||||
if self.task_id.pricing_type in ('task_rate', 'fixed_rate'):
|
||||
return self.task_id.sale_line_id
|
||||
else: # then pricing_type = 'employee_rate'
|
||||
map_entry = self.project_id.sale_line_employee_ids.filtered(
|
||||
lambda map_entry:
|
||||
map_entry.employee_id == self.employee_id
|
||||
and map_entry.sale_line_id.order_partner_id.commercial_partner_id == self.task_id.commercial_partner_id
|
||||
)
|
||||
if map_entry:
|
||||
return map_entry.sale_line_id
|
||||
return self.task_id.sale_line_id
|
||||
return False
|
||||
|
||||
def _timesheet_get_portal_domain(self):
|
||||
""" Only the timesheets with a product invoiced on delivered quantity are concerned.
|
||||
since in ordered quantity, the timesheet quantity is not invoiced,
|
||||
thus there is no meaning of showing invoice with ordered quantity.
|
||||
"""
|
||||
domain = super(AccountAnalyticLine, self)._timesheet_get_portal_domain()
|
||||
return expression.AND([domain, [('timesheet_invoice_type', 'in', ['billable_time', 'non_billable', 'billable_fixed'])]])
|
||||
|
||||
@api.model
|
||||
def _timesheet_get_sale_domain(self, order_lines_ids, invoice_ids):
|
||||
if not invoice_ids:
|
||||
return [('so_line', 'in', order_lines_ids.ids)]
|
||||
|
||||
return [
|
||||
'|',
|
||||
'&',
|
||||
('timesheet_invoice_id', 'in', invoice_ids.ids),
|
||||
# TODO : Master: Check if non_billable should be removed ?
|
||||
('timesheet_invoice_type', 'in', ['billable_time', 'non_billable']),
|
||||
'&',
|
||||
('timesheet_invoice_type', '=', 'billable_fixed'),
|
||||
'&',
|
||||
('so_line', 'in', order_lines_ids.ids),
|
||||
('timesheet_invoice_id', '=', False),
|
||||
]
|
||||
|
||||
def _get_timesheets_to_merge(self):
|
||||
res = super(AccountAnalyticLine, self)._get_timesheets_to_merge()
|
||||
return res.filtered(lambda l: not l.timesheet_invoice_id or l.timesheet_invoice_id.state != 'posted')
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_except_invoiced(self):
|
||||
if any(line.timesheet_invoice_id and line.timesheet_invoice_id.state == 'posted' for line in self):
|
||||
raise UserError(_('You cannot remove a timesheet that has already been invoiced.'))
|
||||
|
||||
def _get_employee_mapping_entry(self):
|
||||
self.ensure_one()
|
||||
return self.env['project.sale.line.employee.map'].search([('project_id', '=', self.project_id.id), ('employee_id', '=', self.employee_id.id or self.env.user.employee_id.id)])
|
||||
|
||||
def _hourly_cost(self):
|
||||
if self.project_id.pricing_type == 'employee_rate':
|
||||
mapping_entry = self._get_employee_mapping_entry()
|
||||
if mapping_entry:
|
||||
return mapping_entry.cost
|
||||
return super()._hourly_cost()
|
||||
|
||||
def _timesheet_convert_sol_uom(self, sol, to_unit):
|
||||
to_uom = self.env.ref(to_unit)
|
||||
return round(sol.product_uom._compute_quantity(sol.product_uom_qty, to_uom, raise_if_failure=False), 2)
|
||||
|
||||
def _is_updatable_timesheet(self):
|
||||
return super()._is_updatable_timesheet and self._is_not_billed()
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
timesheet_ids = fields.One2many('account.analytic.line', 'timesheet_invoice_id', string='Timesheets', readonly=True, copy=False)
|
||||
timesheet_count = fields.Integer("Number of timesheets", compute='_compute_timesheet_count')
|
||||
timesheet_encode_uom_id = fields.Many2one('uom.uom', related='company_id.timesheet_encode_uom_id')
|
||||
timesheet_total_duration = fields.Integer("Timesheet Total Duration", compute='_compute_timesheet_total_duration', help="Total recorded duration, expressed in the encoding UoM, and rounded to the unit")
|
||||
|
||||
@api.depends('timesheet_ids', 'company_id.timesheet_encode_uom_id')
|
||||
def _compute_timesheet_total_duration(self):
|
||||
if not self.user_has_groups('hr_timesheet.group_hr_timesheet_user'):
|
||||
self.timesheet_total_duration = 0
|
||||
return
|
||||
group_data = self.env['account.analytic.line']._read_group([
|
||||
('timesheet_invoice_id', 'in', self.ids)
|
||||
], ['timesheet_invoice_id', 'unit_amount'], ['timesheet_invoice_id'])
|
||||
timesheet_unit_amount_dict = defaultdict(float)
|
||||
timesheet_unit_amount_dict.update({data['timesheet_invoice_id'][0]: data['unit_amount'] for data in group_data})
|
||||
for invoice in self:
|
||||
total_time = invoice.company_id.project_time_mode_id._compute_quantity(
|
||||
timesheet_unit_amount_dict[invoice.id],
|
||||
invoice.timesheet_encode_uom_id,
|
||||
rounding_method='HALF-UP',
|
||||
)
|
||||
invoice.timesheet_total_duration = round(total_time)
|
||||
|
||||
@api.depends('timesheet_ids')
|
||||
def _compute_timesheet_count(self):
|
||||
timesheet_data = self.env['account.analytic.line']._read_group([('timesheet_invoice_id', 'in', self.ids)], ['timesheet_invoice_id'], ['timesheet_invoice_id'])
|
||||
mapped_data = dict([(t['timesheet_invoice_id'][0], t['timesheet_invoice_id_count']) for t in timesheet_data])
|
||||
for invoice in self:
|
||||
invoice.timesheet_count = mapped_data.get(invoice.id, 0)
|
||||
|
||||
def action_view_timesheet(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Timesheets'),
|
||||
'domain': [('project_id', '!=', False)],
|
||||
'res_model': 'account.analytic.line',
|
||||
'view_id': False,
|
||||
'view_mode': 'tree,form',
|
||||
'help': _("""
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Record timesheets
|
||||
</p><p>
|
||||
You can register and track your workings hours by project every
|
||||
day. Every time spent on a project will become a cost and can be re-invoiced to
|
||||
customers if required.
|
||||
</p>
|
||||
"""),
|
||||
'limit': 80,
|
||||
'context': {
|
||||
'default_project_id': self.id,
|
||||
'search_default_project_id': [self.id]
|
||||
}
|
||||
}
|
||||
|
||||
def _link_timesheets_to_invoice(self, start_date=None, end_date=None):
|
||||
""" Search timesheets from given period and link this timesheets to the invoice
|
||||
|
||||
When we create an invoice from a sale order, we need to
|
||||
link the timesheets in this sale order to the invoice.
|
||||
Then, we can know which timesheets are invoiced in the sale order.
|
||||
:param start_date: the start date of the period
|
||||
:param end_date: the end date of the period
|
||||
"""
|
||||
for line in self.filtered(lambda i: i.move_type == 'out_invoice' and i.state == 'draft').invoice_line_ids:
|
||||
sale_line_delivery = line.sale_line_ids.filtered(lambda sol: sol.product_id.invoice_policy == 'delivery' and sol.product_id.service_type == 'timesheet')
|
||||
if sale_line_delivery:
|
||||
domain = line._timesheet_domain_get_invoiced_lines(sale_line_delivery)
|
||||
if start_date:
|
||||
domain = expression.AND([domain, [('date', '>=', start_date)]])
|
||||
if end_date:
|
||||
domain = expression.AND([domain, [('date', '<=', end_date)]])
|
||||
timesheets = self.env['account.analytic.line'].sudo().search(domain)
|
||||
timesheets.write({'timesheet_invoice_id': line.move_id.id})
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
@api.model
|
||||
def _timesheet_domain_get_invoiced_lines(self, sale_line_delivery):
|
||||
""" Get the domain for the timesheet to link to the created invoice
|
||||
:param sale_line_delivery: recordset of sale.order.line to invoice
|
||||
:return a normalized domain
|
||||
"""
|
||||
return [
|
||||
('so_line', 'in', sale_line_delivery.ids),
|
||||
('project_id', '!=', False),
|
||||
'|', '|',
|
||||
('timesheet_invoice_id', '=', False),
|
||||
'&',
|
||||
('timesheet_invoice_id.state', '=', 'cancel'),
|
||||
('timesheet_invoice_id.payment_state', '!=', 'invoicing_legacy'),
|
||||
('timesheet_invoice_id.payment_state', '=', 'reversed')
|
||||
]
|
||||
|
||||
def unlink(self):
|
||||
move_line_read_group = self.env['account.move.line'].search_read([
|
||||
('move_id.move_type', '=', 'out_invoice'),
|
||||
('move_id.state', '=', 'draft'),
|
||||
('sale_line_ids.product_id.invoice_policy', '=', 'delivery'),
|
||||
('sale_line_ids.product_id.service_type', '=', 'timesheet'),
|
||||
('id', 'in', self.ids)],
|
||||
['move_id', 'sale_line_ids'])
|
||||
|
||||
sale_line_ids_per_move = defaultdict(lambda: self.env['sale.order.line'])
|
||||
for move_line in move_line_read_group:
|
||||
sale_line_ids_per_move[move_line['move_id'][0]] += self.env['sale.order.line'].browse(move_line['sale_line_ids'])
|
||||
|
||||
timesheet_read_group = self.sudo().env['account.analytic.line']._read_group([
|
||||
('timesheet_invoice_id.move_type', '=', 'out_invoice'),
|
||||
('timesheet_invoice_id.state', '=', 'draft'),
|
||||
('timesheet_invoice_id', 'in', self.move_id.ids)],
|
||||
['timesheet_invoice_id', 'so_line', 'ids:array_agg(id)'],
|
||||
['timesheet_invoice_id', 'so_line'],
|
||||
lazy=False)
|
||||
|
||||
timesheet_ids = []
|
||||
for timesheet in timesheet_read_group:
|
||||
move_id = timesheet['timesheet_invoice_id'][0]
|
||||
if timesheet['so_line'] and timesheet['so_line'][0] in sale_line_ids_per_move[move_id].ids:
|
||||
timesheet_ids += timesheet['ids']
|
||||
|
||||
self.sudo().env['account.analytic.line'].browse(timesheet_ids).write({'timesheet_invoice_id': False})
|
||||
return super().unlink()
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import threading
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = 'product.template'
|
||||
|
||||
def _selection_service_policy(self):
|
||||
service_policies = super()._selection_service_policy()
|
||||
service_policies.insert(1, ('delivered_timesheet', _('Based on Timesheets')))
|
||||
return service_policies
|
||||
|
||||
service_type = fields.Selection(selection_add=[
|
||||
('timesheet', 'Timesheets on project (one fare per SO/Project)'),
|
||||
], ondelete={'timesheet': 'set manual'})
|
||||
# override domain
|
||||
project_id = fields.Many2one(domain="[('company_id', '=', current_company_id), ('allow_billable', '=', True), ('pricing_type', '=', 'task_rate'), ('allow_timesheets', 'in', [service_policy == 'delivered_timesheet', True])]")
|
||||
project_template_id = fields.Many2one(domain="[('company_id', '=', current_company_id), ('allow_billable', '=', True), ('allow_timesheets', 'in', [service_policy == 'delivered_timesheet', True])]")
|
||||
service_upsell_threshold = fields.Float('Threshold', default=1, help="Percentage of time delivered compared to the prepaid amount that must be reached for the upselling opportunity activity to be triggered.")
|
||||
service_upsell_threshold_ratio = fields.Char(compute='_compute_service_upsell_threshold_ratio')
|
||||
|
||||
@api.depends('uom_id', 'company_id')
|
||||
def _compute_service_upsell_threshold_ratio(self):
|
||||
product_uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
uom_unit = self.env.ref('uom.product_uom_unit')
|
||||
company_uom = self.env.company.timesheet_encode_uom_id
|
||||
for record in self:
|
||||
if not record.uom_id or record.uom_id != uom_unit or\
|
||||
product_uom_hour.factor == record.uom_id.factor or\
|
||||
record.uom_id.category_id not in [product_uom_hour.category_id, uom_unit.category_id]:
|
||||
record.service_upsell_threshold_ratio = False
|
||||
continue
|
||||
else:
|
||||
timesheet_encode_uom = record.company_id.timesheet_encode_uom_id or company_uom
|
||||
record.service_upsell_threshold_ratio = f'(1 {record.uom_id.name} = {timesheet_encode_uom.factor / product_uom_hour.factor:.2f} {timesheet_encode_uom.name})'
|
||||
|
||||
def _compute_visible_expense_policy(self):
|
||||
visibility = self.user_has_groups('project.group_project_user')
|
||||
for product_template in self:
|
||||
if not product_template.visible_expense_policy:
|
||||
product_template.visible_expense_policy = visibility
|
||||
return super(ProductTemplate, self)._compute_visible_expense_policy()
|
||||
|
||||
@api.depends('service_tracking', 'service_policy', 'type')
|
||||
def _compute_product_tooltip(self):
|
||||
super()._compute_product_tooltip()
|
||||
for record in self.filtered(lambda record: record.type == 'service'):
|
||||
if record.service_policy == 'delivered_timesheet':
|
||||
if record.service_tracking == 'no':
|
||||
record.product_tooltip = _(
|
||||
"Invoice based on timesheets (delivered quantity) on projects or tasks "
|
||||
"you'll create later on."
|
||||
)
|
||||
elif record.service_tracking == 'task_global_project':
|
||||
record.product_tooltip = _(
|
||||
"Invoice based on timesheets (delivered quantity), and create a task in "
|
||||
"an existing project to track the time spent."
|
||||
)
|
||||
elif record.service_tracking == 'task_in_project':
|
||||
record.product_tooltip = _(
|
||||
"Invoice based on timesheets (delivered quantity), and create a project "
|
||||
"for the order with a task for each sales order line to track the time "
|
||||
"spent."
|
||||
)
|
||||
elif record.service_tracking == 'project_only':
|
||||
record.product_tooltip = _(
|
||||
"Invoice based on timesheets (delivered quantity), and create an empty "
|
||||
"project for the order to track the time spent."
|
||||
)
|
||||
|
||||
def _get_service_to_general_map(self):
|
||||
return {
|
||||
**super()._get_service_to_general_map(),
|
||||
'delivered_timesheet': ('delivery', 'timesheet'),
|
||||
'ordered_prepaid': ('order', 'timesheet'),
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _get_onchange_service_policy_updates(self, service_tracking, service_policy, project_id, project_template_id):
|
||||
vals = {}
|
||||
if service_tracking != 'no' and service_policy == 'delivered_timesheet':
|
||||
if project_id and not project_id.allow_timesheets:
|
||||
vals['project_id'] = False
|
||||
elif project_template_id and not project_template_id.allow_timesheets:
|
||||
vals['project_template_id'] = False
|
||||
return vals
|
||||
|
||||
@api.onchange('service_policy')
|
||||
def _onchange_service_policy(self):
|
||||
self._inverse_service_policy()
|
||||
vals = self._get_onchange_service_policy_updates(self.service_tracking,
|
||||
self.service_policy,
|
||||
self.project_id,
|
||||
self.project_template_id)
|
||||
if vals:
|
||||
self.update(vals)
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_except_master_data(self):
|
||||
time_product = self.env.ref('sale_timesheet.time_product')
|
||||
if time_product.product_tmpl_id in self:
|
||||
raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.', time_product.name))
|
||||
|
||||
def write(self, vals):
|
||||
# timesheet product can't be archived or linked to a company
|
||||
test_mode = getattr(threading.current_thread(), 'testing', False) or self.env.registry.in_test_mode()
|
||||
if not test_mode and 'active' in vals and not vals['active']:
|
||||
time_product = self.env.ref('sale_timesheet.time_product')
|
||||
if time_product.product_tmpl_id in self:
|
||||
raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.', time_product.name))
|
||||
# TODO: avoid duplicate code by joining both conditions in master
|
||||
if not test_mode and 'company_id' in vals and vals['company_id']:
|
||||
time_product = self.env.ref('sale_timesheet.time_product')
|
||||
if time_product.product_tmpl_id in self:
|
||||
raise ValidationError(_('The %s product is required by the Timesheets app and cannot be linked to a company.', time_product.name))
|
||||
return super(ProductTemplate, self).write(vals)
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = 'product.product'
|
||||
|
||||
def _is_delivered_timesheet(self):
|
||||
""" Check if the product is a delivered timesheet """
|
||||
self.ensure_one()
|
||||
return self.type == 'service' and self.service_policy == 'delivered_timesheet'
|
||||
|
||||
@api.onchange('service_policy')
|
||||
def _onchange_service_policy(self):
|
||||
self._inverse_service_policy()
|
||||
vals = self.product_tmpl_id._get_onchange_service_policy_updates(self.service_tracking,
|
||||
self.service_policy,
|
||||
self.project_id,
|
||||
self.project_template_id)
|
||||
if vals:
|
||||
self.update(vals)
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_except_master_data(self):
|
||||
time_product = self.env.ref('sale_timesheet.time_product')
|
||||
if time_product in self:
|
||||
raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.') % time_product.name)
|
||||
|
||||
def write(self, vals):
|
||||
# timesheet product can't be archived
|
||||
test_mode = getattr(threading.current_thread(), 'testing', False) or self.env.registry.in_test_mode()
|
||||
if not test_mode and 'active' in vals and not vals['active']:
|
||||
time_product = self.env.ref('sale_timesheet.time_product')
|
||||
if time_product in self:
|
||||
raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.') % time_product.name)
|
||||
return super(ProductProduct, self).write(vals)
|
||||
|
|
@ -0,0 +1,617 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import api, fields, models, _, _lt
|
||||
from odoo.osv import expression
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
|
||||
# YTI PLEASE SPLIT ME
|
||||
class Project(models.Model):
|
||||
_inherit = 'project.project'
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
""" Pre-fill timesheet product as "Time" data product when creating new project allowing billable tasks by default. """
|
||||
result = super(Project, self).default_get(fields)
|
||||
if 'timesheet_product_id' in fields and result.get('allow_billable') and result.get('allow_timesheets') and not result.get('timesheet_product_id'):
|
||||
default_product = self.env.ref('sale_timesheet.time_product', False)
|
||||
if default_product:
|
||||
result['timesheet_product_id'] = default_product.id
|
||||
return result
|
||||
|
||||
def _default_timesheet_product_id(self):
|
||||
return self.env.ref('sale_timesheet.time_product', False)
|
||||
|
||||
pricing_type = fields.Selection([
|
||||
('task_rate', 'Task rate'),
|
||||
('fixed_rate', 'Project rate'),
|
||||
('employee_rate', 'Employee rate')
|
||||
], string="Pricing", default="task_rate",
|
||||
compute='_compute_pricing_type',
|
||||
search='_search_pricing_type',
|
||||
help='The task rate is perfect if you would like to bill different services to different customers at different rates. The fixed rate is perfect if you bill a service at a fixed rate per hour or day worked regardless of the employee who performed it. The employee rate is preferable if your employees deliver the same service at a different rate. For instance, junior and senior consultants would deliver the same service (= consultancy), but at a different rate because of their level of seniority.')
|
||||
sale_line_employee_ids = fields.One2many('project.sale.line.employee.map', 'project_id', "Sale line/Employee map", copy=False,
|
||||
help="Sales order item that will be selected by default on the timesheets of the corresponding employee. It bypasses the sales order item defined on the project and the task, and can be modified on each timesheet entry if necessary. In other words, it defines the rate at which an employee's time is billed based on their expertise, skills or experience, for instance.\n"
|
||||
"If you would like to bill the same service at a different rate, you need to create two separate sales order items as each sales order item can only have a single unit price at a time.\n"
|
||||
"You can also define the hourly company cost of your employees for their timesheets on this project specifically. It will bypass the timesheet cost set on the employee.")
|
||||
billable_percentage = fields.Integer(
|
||||
compute='_compute_billable_percentage', groups='hr_timesheet.group_hr_timesheet_approver',
|
||||
help="% of timesheets that are billable compared to the total number of timesheets linked to the AA of the project, rounded to the unit.")
|
||||
display_create_order = fields.Boolean(compute='_compute_display_create_order')
|
||||
timesheet_product_id = fields.Many2one(
|
||||
'product.product', string='Timesheet Product',
|
||||
domain="""[
|
||||
('detailed_type', '=', 'service'),
|
||||
('invoice_policy', '=', 'delivery'),
|
||||
('service_type', '=', 'timesheet'),
|
||||
'|', ('company_id', '=', False), ('company_id', '=', company_id)]""",
|
||||
help='Service that will be used by default when invoicing the time spent on a task. It can be modified on each task individually by selecting a specific sales order item.',
|
||||
compute="_compute_timesheet_product_id", store=True, readonly=False,
|
||||
default=_default_timesheet_product_id)
|
||||
warning_employee_rate = fields.Boolean(compute='_compute_warning_employee_rate', compute_sudo=True)
|
||||
partner_id = fields.Many2one(
|
||||
compute='_compute_partner_id', store=True, readonly=False)
|
||||
allocated_hours = fields.Float(compute='_compute_allocated_hours', store=True, readonly=False)
|
||||
|
||||
@api.model
|
||||
def _get_view(self, view_id=None, view_type='form', **options):
|
||||
arch, view = super()._get_view(view_id, view_type, **options)
|
||||
if view_type == 'form' and self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day'):
|
||||
for node in arch.xpath("//field[@name='display_cost'][not(@string)]"):
|
||||
node.set('string', 'Daily Cost')
|
||||
return arch, view
|
||||
|
||||
@api.depends('sale_line_id', 'sale_line_employee_ids', 'allow_billable')
|
||||
def _compute_pricing_type(self):
|
||||
billable_projects = self.filtered('allow_billable')
|
||||
for project in billable_projects:
|
||||
if project.sale_line_employee_ids:
|
||||
project.pricing_type = 'employee_rate'
|
||||
elif project.sale_line_id:
|
||||
project.pricing_type = 'fixed_rate'
|
||||
else:
|
||||
project.pricing_type = 'task_rate'
|
||||
(self - billable_projects).update({'pricing_type': False})
|
||||
|
||||
def _search_pricing_type(self, operator, value):
|
||||
""" Search method for pricing_type field.
|
||||
|
||||
This method returns a domain based on the operator and the value given in parameter:
|
||||
- operator = '=':
|
||||
- value = 'task_rate': [('sale_line_employee_ids', '=', False), ('sale_line_id', '=', False), ('allow_billable', '=', True)]
|
||||
- value = 'fixed_rate': [('sale_line_employee_ids', '=', False), ('sale_line_id', '!=', False), ('allow_billable', '=', True)]
|
||||
- value = 'employee_rate': [('sale_line_employee_ids', '!=', False), ('allow_billable', '=', True)]
|
||||
- value is False: [('allow_billable', '=', False)]
|
||||
- operator = '!=':
|
||||
- value = 'task_rate': ['|', '|', ('sale_line_employee_ids', '!=', False), ('sale_line_id', '!=', False), ('allow_billable', '=', False)]
|
||||
- value = 'fixed_rate': ['|', '|', ('sale_line_employee_ids', '!=', False), ('sale_line_id', '=', False), ('allow_billable', '=', False)]
|
||||
- value = 'employee_rate': ['|', ('sale_line_employee_ids', '=', False), ('allow_billable', '=', False)]
|
||||
- value is False: [('allow_billable', '!=', False)]
|
||||
|
||||
:param operator: the supported operator is either '=' or '!='.
|
||||
:param value: the value than the field should be is among these values into the following tuple: (False, 'task_rate', 'fixed_rate', 'employee_rate').
|
||||
|
||||
:returns: the domain to find the expected projects.
|
||||
"""
|
||||
if operator not in ('=', '!='):
|
||||
raise UserError(_('Operation not supported'))
|
||||
if not ((isinstance(value, bool) and value is False) or (isinstance(value, str) and value in ('task_rate', 'fixed_rate', 'employee_rate'))):
|
||||
raise UserError(_('Value does not exist in the pricing type'))
|
||||
if value is False:
|
||||
return [('allow_billable', operator, value)]
|
||||
|
||||
sol_cond = ('sale_line_id', '!=', False)
|
||||
mapping_cond = ('sale_line_employee_ids', '!=', False)
|
||||
if value == 'task_rate':
|
||||
domain = [expression.NOT_OPERATOR, sol_cond, expression.NOT_OPERATOR, mapping_cond]
|
||||
elif value == 'fixed_rate':
|
||||
domain = [sol_cond, expression.NOT_OPERATOR, mapping_cond]
|
||||
else: # value == 'employee_rate'
|
||||
domain = [mapping_cond]
|
||||
|
||||
domain = expression.AND([domain, [('allow_billable', '=', True)]])
|
||||
domain = expression.normalize_domain(domain)
|
||||
if operator != '=':
|
||||
domain.insert(0, expression.NOT_OPERATOR)
|
||||
domain = expression.distribute_not(domain)
|
||||
return domain
|
||||
|
||||
@api.depends('analytic_account_id', 'timesheet_ids')
|
||||
def _compute_billable_percentage(self):
|
||||
timesheets_read_group = self.env['account.analytic.line']._read_group([('project_id', 'in', self.ids)], ['project_id', 'so_line', 'unit_amount'], ['project_id', 'so_line'], lazy=False)
|
||||
timesheets_by_project = defaultdict(list)
|
||||
for res in timesheets_read_group:
|
||||
timesheets_by_project[res['project_id'][0]].append((res['unit_amount'], bool(res['so_line'])))
|
||||
for project in self:
|
||||
timesheet_total = timesheet_billable = 0.0
|
||||
for unit_amount, is_billable_timesheet in timesheets_by_project[project.id]:
|
||||
timesheet_total += unit_amount
|
||||
if is_billable_timesheet:
|
||||
timesheet_billable += unit_amount
|
||||
billable_percentage = timesheet_billable / timesheet_total * 100 if timesheet_total > 0 else 0
|
||||
project.billable_percentage = round(billable_percentage)
|
||||
|
||||
@api.depends('partner_id', 'pricing_type')
|
||||
def _compute_display_create_order(self):
|
||||
for project in self:
|
||||
project.display_create_order = project.partner_id and project.pricing_type == 'task_rate'
|
||||
|
||||
@api.depends('allow_timesheets', 'allow_billable')
|
||||
def _compute_timesheet_product_id(self):
|
||||
default_product = self.env.ref('sale_timesheet.time_product', False)
|
||||
for project in self:
|
||||
if not project.allow_timesheets or not project.allow_billable:
|
||||
project.timesheet_product_id = False
|
||||
elif not project.timesheet_product_id:
|
||||
project.timesheet_product_id = default_product
|
||||
|
||||
@api.depends('pricing_type', 'allow_timesheets', 'allow_billable', 'sale_line_employee_ids', 'sale_line_employee_ids.employee_id')
|
||||
def _compute_warning_employee_rate(self):
|
||||
projects = self.filtered(lambda p: p.allow_billable and p.allow_timesheets and p.pricing_type == 'employee_rate')
|
||||
employees = self.env['account.analytic.line']._read_group([('task_id', 'in', projects.task_ids.ids)], ['employee_id', 'project_id'], ['employee_id', 'project_id'], lazy=False)
|
||||
dict_project_employee = defaultdict(list)
|
||||
for line in employees:
|
||||
dict_project_employee[line['project_id'][0]] += [line['employee_id'][0]] if line['employee_id'] else []
|
||||
for project in projects:
|
||||
project.warning_employee_rate = any(x not in project.sale_line_employee_ids.employee_id.ids for x in dict_project_employee[project.id])
|
||||
|
||||
(self - projects).warning_employee_rate = False
|
||||
|
||||
@api.depends('sale_line_employee_ids.sale_line_id', 'sale_line_id')
|
||||
def _compute_partner_id(self):
|
||||
for project in self:
|
||||
if project.partner_id:
|
||||
continue
|
||||
if project.allow_billable and project.allow_timesheets and project.pricing_type != 'task_rate':
|
||||
sol = project.sale_line_id or project.sale_line_employee_ids.sale_line_id[:1]
|
||||
project.partner_id = sol.order_partner_id
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_sale_line_id(self):
|
||||
super()._compute_sale_line_id()
|
||||
for project in self.filtered(lambda p: not p.sale_line_id and p.partner_id and p.pricing_type == 'employee_rate'):
|
||||
# Give a SOL by default either the last SOL with service product and remaining_hours > 0
|
||||
sol = self.env['sale.order.line'].search([
|
||||
('is_service', '=', True),
|
||||
('order_partner_id', 'child_of', project.partner_id.commercial_partner_id.id),
|
||||
('is_expense', '=', False),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('remaining_hours', '>', 0)
|
||||
], limit=1)
|
||||
project.sale_line_id = sol or project.sale_line_employee_ids.sale_line_id[:1] # get the first SOL containing in the employee mappings if no sol found in the search
|
||||
|
||||
@api.depends('sale_line_id.product_uom_qty', 'sale_line_id.product_uom')
|
||||
def _compute_allocated_hours(self):
|
||||
# TODO: remove in master
|
||||
return
|
||||
|
||||
@api.depends('sale_line_employee_ids.sale_line_id', 'allow_billable')
|
||||
def _compute_sale_order_count(self):
|
||||
billable_projects = self.filtered('allow_billable')
|
||||
super(Project, billable_projects)._compute_sale_order_count()
|
||||
(self - billable_projects).sale_order_count = 0
|
||||
|
||||
@api.constrains('sale_line_id')
|
||||
def _check_sale_line_type(self):
|
||||
for project in self.filtered(lambda project: project.sale_line_id):
|
||||
if not project.sale_line_id.is_service:
|
||||
raise ValidationError(_("You cannot link a billable project to a sales order item that is not a service."))
|
||||
if project.sale_line_id.is_expense:
|
||||
raise ValidationError(_("You cannot link a billable project to a sales order item that comes from an expense or a vendor bill."))
|
||||
|
||||
def write(self, values):
|
||||
res = super(Project, self).write(values)
|
||||
if 'allow_billable' in values and not values.get('allow_billable'):
|
||||
self.task_ids._get_timesheet().write({
|
||||
'so_line': False,
|
||||
})
|
||||
return res
|
||||
|
||||
def _update_timesheets_sale_line_id(self):
|
||||
for project in self.filtered(lambda p: p.allow_billable and p.allow_timesheets):
|
||||
timesheet_ids = project.mapped('timesheet_ids').filtered(lambda t: not t.is_so_line_edited and t._is_updatable_timesheet())
|
||||
if not timesheet_ids:
|
||||
continue
|
||||
for employee_id in project.sale_line_employee_ids.filtered(lambda l: l.project_id == project).employee_id:
|
||||
sale_line_id = project.sale_line_employee_ids.filtered(lambda l: l.project_id == project and l.employee_id == employee_id).sale_line_id
|
||||
timesheet_ids.filtered(lambda t: t.employee_id == employee_id).sudo().so_line = sale_line_id
|
||||
|
||||
def action_view_timesheet(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Timesheets of %s', self.name),
|
||||
'domain': [('project_id', '!=', False)],
|
||||
'res_model': 'account.analytic.line',
|
||||
'view_id': False,
|
||||
'view_mode': 'tree,form',
|
||||
'help': _("""
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Record timesheets
|
||||
</p><p>
|
||||
You can register and track your workings hours by project every
|
||||
day. Every time spent on a project will become a cost and can be re-invoiced to
|
||||
customers if required.
|
||||
</p>
|
||||
"""),
|
||||
'limit': 80,
|
||||
'context': {
|
||||
'default_project_id': self.id,
|
||||
'search_default_project_id': [self.id]
|
||||
}
|
||||
}
|
||||
|
||||
def action_make_billable(self):
|
||||
return {
|
||||
"name": _("Create Sales Order"),
|
||||
"type": 'ir.actions.act_window',
|
||||
"res_model": 'project.create.sale.order',
|
||||
"views": [[False, "form"]],
|
||||
"target": 'new',
|
||||
"context": {
|
||||
'active_id': self.id,
|
||||
'active_model': 'project.project',
|
||||
'default_product_id': self.timesheet_product_id.id,
|
||||
},
|
||||
}
|
||||
|
||||
def action_billable_time_button(self):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("sale_timesheet.timesheet_action_from_sales_order_item")
|
||||
action.update({
|
||||
'context': {
|
||||
'grid_range': 'week',
|
||||
'search_default_groupby_timesheet_invoice_type': True,
|
||||
'default_project_id': self.id,
|
||||
},
|
||||
'domain': [('project_id', '=', self.id)],
|
||||
})
|
||||
return action
|
||||
|
||||
def action_profitability_items(self, section_name, domain=None, res_id=False):
|
||||
self.ensure_one()
|
||||
if section_name in ['billable_fixed', 'billable_time', 'billable_milestones', 'billable_manual', 'non_billable']:
|
||||
action = self.action_billable_time_button()
|
||||
if domain:
|
||||
action['domain'] = expression.AND([[('project_id', '=', self.id)], domain])
|
||||
action['context'].update(search_default_groupby_timesheet_invoice_type=False, **self.env.context)
|
||||
graph_view = False
|
||||
if section_name == 'billable_time':
|
||||
graph_view = self.env.ref('hr_timesheet.view_hr_timesheet_line_graph_all').id
|
||||
action['views'] = [
|
||||
(view_id, view_type) if view_type != 'graph' else (graph_view or view_id, view_type)
|
||||
for view_id, view_type in action['views']
|
||||
]
|
||||
if res_id:
|
||||
if 'views' in action:
|
||||
action['views'] = [
|
||||
(view_id, view_type)
|
||||
for view_id, view_type in action['views']
|
||||
if view_type == 'form'
|
||||
] or [False, 'form']
|
||||
action['view_mode'] = 'form'
|
||||
action['res_id'] = res_id
|
||||
return action
|
||||
return super().action_profitability_items(section_name, domain, res_id)
|
||||
|
||||
# ----------------------------
|
||||
# Project Updates
|
||||
# ----------------------------
|
||||
|
||||
def get_panel_data(self):
|
||||
panel_data = super(Project, self).get_panel_data()
|
||||
return {
|
||||
**panel_data,
|
||||
'analytic_account_id': self.analytic_account_id.id,
|
||||
}
|
||||
|
||||
def _get_sale_order_items_query(self, domain_per_model=None):
|
||||
if domain_per_model is None:
|
||||
domain_per_model = {'project.task': [('allow_billable', '=', True)]}
|
||||
else:
|
||||
domain_per_model['project.task'] = expression.AND([
|
||||
domain_per_model.get('project.task', []),
|
||||
[('allow_billable', '=', True)],
|
||||
])
|
||||
query = super()._get_sale_order_items_query(domain_per_model)
|
||||
|
||||
Timesheet = self.env['account.analytic.line']
|
||||
timesheet_domain = [('project_id', 'in', self.ids), ('so_line', '!=', False), ('project_id.allow_billable', '=', True)]
|
||||
if Timesheet._name in domain_per_model:
|
||||
timesheet_domain = expression.AND([
|
||||
domain_per_model.get(Timesheet._name, []),
|
||||
timesheet_domain,
|
||||
])
|
||||
timesheet_query = Timesheet._where_calc(timesheet_domain)
|
||||
Timesheet._apply_ir_rules(timesheet_query, 'read')
|
||||
timesheet_query_str, timesheet_params = timesheet_query.select(
|
||||
f'{Timesheet._table}.project_id AS id',
|
||||
f'{Timesheet._table}.so_line AS sale_line_id',
|
||||
)
|
||||
|
||||
EmployeeMapping = self.env['project.sale.line.employee.map']
|
||||
employee_mapping_domain = [('project_id', 'in', self.ids), ('project_id.allow_billable', '=', True), ('sale_line_id', '!=', False)]
|
||||
if EmployeeMapping._name in domain_per_model:
|
||||
employee_mapping_domain = expression.AND([
|
||||
domain_per_model[EmployeeMapping._name],
|
||||
employee_mapping_domain,
|
||||
])
|
||||
employee_mapping_query = EmployeeMapping._where_calc(employee_mapping_domain)
|
||||
EmployeeMapping._apply_ir_rules(employee_mapping_query, 'read')
|
||||
employee_mapping_query_str, employee_mapping_params = employee_mapping_query.select(
|
||||
f'{EmployeeMapping._table}.project_id AS id',
|
||||
f'{EmployeeMapping._table}.sale_line_id',
|
||||
)
|
||||
|
||||
query._tables['project_sale_order_item'] = ' UNION '.join([
|
||||
query._tables['project_sale_order_item'],
|
||||
timesheet_query_str,
|
||||
employee_mapping_query_str,
|
||||
])
|
||||
query._where_params += timesheet_params + employee_mapping_params
|
||||
return query
|
||||
|
||||
def _get_profitability_labels(self):
|
||||
return {
|
||||
**super()._get_profitability_labels(),
|
||||
'billable_fixed': _lt('Timesheets (Fixed Price)'),
|
||||
'billable_time': _lt('Timesheets (Billed on Timesheets)'),
|
||||
'billable_milestones': _lt('Timesheets (Billed on Milestones)'),
|
||||
'billable_manual': _lt('Timesheets (Billed Manually)'),
|
||||
'non_billable': _lt('Timesheets (Non Billable)'),
|
||||
'timesheet_revenues': _lt('Timesheets revenues'),
|
||||
'other_costs': _lt('Materials'),
|
||||
}
|
||||
|
||||
def _get_profitability_sequence_per_invoice_type(self):
|
||||
return {
|
||||
**super()._get_profitability_sequence_per_invoice_type(),
|
||||
'billable_fixed': 1,
|
||||
'billable_time': 2,
|
||||
'billable_milestones': 3,
|
||||
'billable_manual': 4,
|
||||
'non_billable': 5,
|
||||
'timesheet_revenues': 6,
|
||||
'other_costs': 12,
|
||||
}
|
||||
|
||||
def _get_profitability_aal_domain(self):
|
||||
domain = ['|', ('project_id', 'in', self.ids), ('so_line', 'in', self._fetch_sale_order_item_ids())]
|
||||
return expression.AND([
|
||||
super()._get_profitability_aal_domain(),
|
||||
domain,
|
||||
])
|
||||
|
||||
def _get_profitability_items_from_aal(self, profitability_items, with_action=True):
|
||||
if not self.allow_timesheets:
|
||||
total_invoiced = total_to_invoice = 0.0
|
||||
revenue_data = []
|
||||
for revenue in profitability_items['revenues']['data']:
|
||||
if revenue['id'] in ['billable_fixed', 'billable_time', 'billable_milestones', 'billable_manual']:
|
||||
continue
|
||||
total_invoiced += revenue['invoiced']
|
||||
total_to_invoice += revenue['to_invoice']
|
||||
revenue_data.append(revenue)
|
||||
profitability_items['revenues'] = {
|
||||
'data': revenue_data,
|
||||
'total': {'to_invoice': total_to_invoice, 'invoiced': total_invoiced},
|
||||
}
|
||||
return profitability_items
|
||||
aa_line_read_group = self.env['account.analytic.line'].sudo()._read_group(
|
||||
self.sudo()._get_profitability_aal_domain(),
|
||||
['timesheet_invoice_type', 'timesheet_invoice_id', 'unit_amount', 'amount', 'ids:array_agg(id)'],
|
||||
['timesheet_invoice_type', 'timesheet_invoice_id'],
|
||||
lazy=False)
|
||||
can_see_timesheets = with_action and len(self) == 1 and self.user_has_groups('hr_timesheet.group_hr_timesheet_approver')
|
||||
revenues_dict = {}
|
||||
costs_dict = {}
|
||||
total_revenues = {'invoiced': 0.0, 'to_invoice': 0.0}
|
||||
total_costs = {'billed': 0.0, 'to_bill': 0.0}
|
||||
for res in aa_line_read_group:
|
||||
amount = res['amount']
|
||||
invoice_type = res['timesheet_invoice_type']
|
||||
cost = costs_dict.setdefault(invoice_type, {'billed': 0.0, 'to_bill': 0.0})
|
||||
revenue = revenues_dict.setdefault(invoice_type, {'invoiced': 0.0, 'to_invoice': 0.0})
|
||||
if amount < 0: # cost
|
||||
cost['billed'] += amount
|
||||
total_costs['billed'] += amount
|
||||
else: # revenues
|
||||
revenue['invoiced'] += amount
|
||||
total_revenues['invoiced'] += amount
|
||||
if can_see_timesheets and invoice_type not in ['other_costs', 'other_revenues']:
|
||||
cost.setdefault('record_ids', []).extend(res['ids'])
|
||||
revenue.setdefault('record_ids', []).extend(res['ids'])
|
||||
|
||||
action_name = None
|
||||
if can_see_timesheets:
|
||||
action_name = 'action_profitability_items'
|
||||
|
||||
def get_timesheets_action(invoice_type, record_ids):
|
||||
args = [invoice_type, [('id', 'in', record_ids)]]
|
||||
if len(record_ids) == 1:
|
||||
args.append(record_ids[0])
|
||||
return {'name': action_name, 'type': 'object', 'args': json.dumps(args)}
|
||||
|
||||
sequence_per_invoice_type = self._get_profitability_sequence_per_invoice_type()
|
||||
|
||||
def convert_dict_into_profitability_data(d, cost=True):
|
||||
profitability_data = []
|
||||
key1, key2 = ['to_bill', 'billed'] if cost else ['to_invoice', 'invoiced']
|
||||
for invoice_type, vals in d.items():
|
||||
if not vals[key1] and not vals[key2]:
|
||||
continue
|
||||
record_ids = vals.pop('record_ids', [])
|
||||
data = {'id': invoice_type, 'sequence': sequence_per_invoice_type[invoice_type], **vals}
|
||||
if record_ids:
|
||||
if invoice_type not in ['other_costs', 'other_revenues'] and can_see_timesheets: # action to see the timesheets
|
||||
action = get_timesheets_action(invoice_type, record_ids)
|
||||
action['context'] = json.dumps({'search_default_groupby_invoice': 1 if not cost and invoice_type == 'billable_time' else 0})
|
||||
data['action'] = action
|
||||
profitability_data.append(data)
|
||||
return profitability_data
|
||||
|
||||
def merge_profitability_data(a, b):
|
||||
return {
|
||||
'data': a['data'] + b['data'],
|
||||
'total': {key: a['total'][key] + b['total'][key] for key in a['total'].keys() if key in b['total']}
|
||||
}
|
||||
|
||||
for revenue in profitability_items['revenues']['data']:
|
||||
revenue_id = revenue['id']
|
||||
aal_revenue = revenues_dict.pop(revenue_id, {})
|
||||
revenue['to_invoice'] += aal_revenue.get('to_invoice', 0.0)
|
||||
revenue['invoiced'] += aal_revenue.get('invoiced', 0.0)
|
||||
record_ids = aal_revenue.get('record_ids', [])
|
||||
if can_see_timesheets and record_ids:
|
||||
action = get_timesheets_action(revenue_id, record_ids)
|
||||
action['context'] = json.dumps({'search_default_groupby_invoice': 1 if revenue_id == 'billable_time' else 0})
|
||||
revenue['action'] = action
|
||||
|
||||
for cost in profitability_items['costs']['data']:
|
||||
cost_id = cost['id']
|
||||
aal_cost = costs_dict.pop(cost_id, {})
|
||||
cost['to_bill'] += aal_cost.get('to_bill', 0.0)
|
||||
cost['billed'] += aal_cost.get('billed', 0.0)
|
||||
record_ids = aal_cost.get('record_ids', [])
|
||||
if can_see_timesheets and record_ids:
|
||||
cost['action'] = get_timesheets_action(cost_id, record_ids)
|
||||
|
||||
profitability_items['revenues'] = merge_profitability_data(
|
||||
profitability_items['revenues'],
|
||||
{'data': convert_dict_into_profitability_data(revenues_dict, False), 'total': total_revenues},
|
||||
)
|
||||
profitability_items['costs'] = merge_profitability_data(
|
||||
profitability_items['costs'],
|
||||
{'data': convert_dict_into_profitability_data(costs_dict), 'total': total_costs},
|
||||
)
|
||||
return profitability_items
|
||||
|
||||
def _get_service_policy_to_invoice_type(self):
|
||||
return {
|
||||
**super()._get_service_policy_to_invoice_type(),
|
||||
'ordered_prepaid': 'billable_fixed',
|
||||
'delivered_milestones': 'billable_milestones',
|
||||
'delivered_timesheet': 'billable_time',
|
||||
'delivered_manual': 'billable_manual',
|
||||
}
|
||||
|
||||
def _get_profitability_items(self, with_action=True):
|
||||
return self._get_profitability_items_from_aal(
|
||||
super()._get_profitability_items(with_action),
|
||||
with_action
|
||||
)
|
||||
|
||||
|
||||
class ProjectTask(models.Model):
|
||||
_inherit = "project.task"
|
||||
|
||||
def _get_default_partner_id(self, project, parent):
|
||||
res = super()._get_default_partner_id(project, parent)
|
||||
if not res and project:
|
||||
# project in sudo if the current user is a portal user.
|
||||
related_project = project if not self.user_has_groups('!base.group_user,base.group_portal') else project.sudo()
|
||||
if related_project.pricing_type == 'employee_rate':
|
||||
return related_project.sale_line_employee_ids.sale_line_id.order_partner_id[:1]
|
||||
return res
|
||||
|
||||
sale_order_id = fields.Many2one(domain="['|', '|', ('partner_id', '=', partner_id), ('partner_id', 'child_of', commercial_partner_id), ('partner_id', 'parent_of', partner_id)]")
|
||||
so_analytic_account_id = fields.Many2one(related='sale_order_id.analytic_account_id', string='Sale Order Analytic Account')
|
||||
pricing_type = fields.Selection(related="project_id.pricing_type")
|
||||
is_project_map_empty = fields.Boolean("Is Project map empty", compute='_compute_is_project_map_empty')
|
||||
has_multi_sol = fields.Boolean(compute='_compute_has_multi_sol', compute_sudo=True)
|
||||
allow_billable = fields.Boolean(related="project_id.allow_billable")
|
||||
timesheet_product_id = fields.Many2one(related="project_id.timesheet_product_id")
|
||||
remaining_hours_so = fields.Float('Remaining Hours on SO', compute='_compute_remaining_hours_so', search='_search_remaining_hours_so', compute_sudo=True)
|
||||
remaining_hours_available = fields.Boolean(related="sale_line_id.remaining_hours_available")
|
||||
|
||||
@property
|
||||
def SELF_READABLE_FIELDS(self):
|
||||
return super().SELF_READABLE_FIELDS | {
|
||||
'allow_billable',
|
||||
'remaining_hours_available',
|
||||
'remaining_hours_so',
|
||||
}
|
||||
|
||||
@api.depends('sale_line_id', 'timesheet_ids', 'timesheet_ids.unit_amount')
|
||||
def _compute_remaining_hours_so(self):
|
||||
# TODO This is not yet perfectly working as timesheet.so_line stick to its old value although changed
|
||||
# in the task From View.
|
||||
timesheets = self.timesheet_ids.filtered(lambda t: t.task_id.sale_line_id in (t.so_line, t._origin.so_line) and t.so_line.remaining_hours_available)
|
||||
|
||||
mapped_remaining_hours = {task._origin.id: task.sale_line_id and task.sale_line_id.remaining_hours or 0.0 for task in self}
|
||||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
for timesheet in timesheets:
|
||||
delta = 0
|
||||
if timesheet._origin.so_line == timesheet.task_id.sale_line_id:
|
||||
delta += timesheet._origin.unit_amount
|
||||
if timesheet.so_line == timesheet.task_id.sale_line_id:
|
||||
delta -= timesheet.unit_amount
|
||||
if delta:
|
||||
mapped_remaining_hours[timesheet.task_id._origin.id] += timesheet.product_uom_id._compute_quantity(delta, uom_hour)
|
||||
|
||||
for task in self:
|
||||
task.remaining_hours_so = mapped_remaining_hours[task._origin.id]
|
||||
|
||||
@api.model
|
||||
def _search_remaining_hours_so(self, operator, value):
|
||||
return [('sale_line_id.remaining_hours', operator, value)]
|
||||
|
||||
@api.depends('so_analytic_account_id.active')
|
||||
def _compute_analytic_account_active(self):
|
||||
super()._compute_analytic_account_active()
|
||||
for task in self:
|
||||
task.analytic_account_active = task.analytic_account_active or task.so_analytic_account_id.active
|
||||
|
||||
@api.depends('allow_billable')
|
||||
def _compute_sale_order_id(self):
|
||||
billable_tasks = self.filtered('allow_billable')
|
||||
super(ProjectTask, billable_tasks)._compute_sale_order_id()
|
||||
(self - billable_tasks).sale_order_id = False
|
||||
|
||||
@api.depends('commercial_partner_id', 'sale_line_id.order_partner_id', 'parent_id.sale_line_id', 'project_id.sale_line_id', 'allow_billable')
|
||||
def _compute_sale_line(self):
|
||||
billable_tasks = self.filtered('allow_billable')
|
||||
(self - billable_tasks).update({'sale_line_id': False})
|
||||
super(ProjectTask, billable_tasks)._compute_sale_line()
|
||||
for task in billable_tasks:
|
||||
if not task.sale_line_id:
|
||||
task.sale_line_id = task._get_last_sol_of_customer()
|
||||
|
||||
@api.depends('project_id.sale_line_employee_ids')
|
||||
def _compute_is_project_map_empty(self):
|
||||
for task in self:
|
||||
task.is_project_map_empty = not bool(task.sudo().project_id.sale_line_employee_ids)
|
||||
|
||||
@api.depends('timesheet_ids')
|
||||
def _compute_has_multi_sol(self):
|
||||
for task in self:
|
||||
task.has_multi_sol = task.timesheet_ids and task.timesheet_ids.so_line != task.sale_line_id
|
||||
|
||||
def _get_last_sol_of_customer(self):
|
||||
# Get the last SOL made for the customer in the current task where we need to compute
|
||||
self.ensure_one()
|
||||
if not self.commercial_partner_id or not self.allow_billable:
|
||||
return False
|
||||
domain = [('company_id', '=', self.company_id.id), ('is_service', '=', True), ('order_partner_id', 'child_of', self.commercial_partner_id.id), ('is_expense', '=', False), ('state', 'in', ['sale', 'done']), ('remaining_hours', '>', 0)]
|
||||
if self.project_id.pricing_type != 'task_rate' and self.project_sale_order_id and self.commercial_partner_id == self.project_id.partner_id.commercial_partner_id:
|
||||
domain.append(('order_id', '=?', self.project_sale_order_id.id))
|
||||
return self.env['sale.order.line'].search(domain, limit=1)
|
||||
|
||||
def _get_timesheet(self):
|
||||
# return not invoiced timesheet and timesheet without so_line or so_line linked to task
|
||||
timesheet_ids = super(ProjectTask, self)._get_timesheet()
|
||||
return timesheet_ids.filtered(lambda t: t._is_not_billed())
|
||||
|
||||
def _get_action_view_so_ids(self):
|
||||
return list(set((self.sale_order_id + self.timesheet_ids.so_line.order_id).ids))
|
||||
|
||||
class ProjectTaskRecurrence(models.Model):
|
||||
_inherit = 'project.task.recurrence'
|
||||
|
||||
@api.model
|
||||
def _get_recurring_fields(self):
|
||||
return ['so_analytic_account_id'] + super(ProjectTaskRecurrence, self)._get_recurring_fields()
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.osv import expression
|
||||
from odoo.tools.misc import unquote
|
||||
|
||||
|
||||
class ProjectProductEmployeeMap(models.Model):
|
||||
_name = 'project.sale.line.employee.map'
|
||||
_description = 'Project Sales line, employee mapping'
|
||||
|
||||
def _domain_sale_line_id(self):
|
||||
domain = expression.AND([
|
||||
self.env['sale.order.line']._sellable_lines_domain(),
|
||||
[
|
||||
('is_service', '=', True),
|
||||
('is_expense', '=', False),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('order_partner_id', '=?', unquote('partner_id')),
|
||||
'|', ('company_id', '=', False), ('company_id', '=', unquote('company_id')),
|
||||
],
|
||||
])
|
||||
return domain
|
||||
|
||||
project_id = fields.Many2one('project.project', "Project", required=True)
|
||||
employee_id = fields.Many2one('hr.employee', "Employee", required=True)
|
||||
sale_line_id = fields.Many2one(
|
||||
'sale.order.line', "Sales Order Item",
|
||||
compute="_compute_sale_line_id", store=True, readonly=False,
|
||||
domain=lambda self: str(self._domain_sale_line_id()))
|
||||
company_id = fields.Many2one('res.company', string='Company', related='project_id.company_id')
|
||||
partner_id = fields.Many2one(related='project_id.partner_id')
|
||||
price_unit = fields.Float("Unit Price", compute='_compute_price_unit', store=True, readonly=True)
|
||||
currency_id = fields.Many2one('res.currency', string="Currency", compute='_compute_currency_id', store=True, readonly=False)
|
||||
cost = fields.Monetary(currency_field='cost_currency_id', compute='_compute_cost', store=True, readonly=False,
|
||||
help="This cost overrides the employee's default employee hourly wage in employee's HR Settings")
|
||||
display_cost = fields.Monetary(currency_field='cost_currency_id', compute="_compute_display_cost", inverse="_inverse_display_cost", string="Hourly Cost")
|
||||
cost_currency_id = fields.Many2one('res.currency', string="Cost Currency", related='employee_id.currency_id', readonly=True)
|
||||
is_cost_changed = fields.Boolean('Is Cost Manually Changed', compute='_compute_is_cost_changed', store=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('uniqueness_employee', 'UNIQUE(project_id,employee_id)', 'An employee cannot be selected more than once in the mapping. Please remove duplicate(s) and try again.'),
|
||||
]
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_sale_line_id(self):
|
||||
self.filtered(
|
||||
lambda map_entry:
|
||||
map_entry.sale_line_id
|
||||
and map_entry.partner_id
|
||||
and map_entry.sale_line_id.order_partner_id.commercial_partner_id != map_entry.partner_id.commercial_partner_id
|
||||
).update({'sale_line_id': False})
|
||||
|
||||
@api.depends('sale_line_id.price_unit')
|
||||
def _compute_price_unit(self):
|
||||
for line in self:
|
||||
if line.sale_line_id:
|
||||
line.price_unit = line.sale_line_id.price_unit
|
||||
else:
|
||||
line.price_unit = 0
|
||||
|
||||
@api.depends('sale_line_id.price_unit')
|
||||
def _compute_currency_id(self):
|
||||
for line in self:
|
||||
line.currency_id = line.sale_line_id.currency_id if line.sale_line_id else False
|
||||
|
||||
@api.depends('employee_id.hourly_cost')
|
||||
def _compute_cost(self):
|
||||
self.env.remove_to_compute(self._fields['is_cost_changed'], self)
|
||||
for map_entry in self:
|
||||
if not map_entry.is_cost_changed:
|
||||
map_entry.cost = map_entry.employee_id.hourly_cost or 0.0
|
||||
|
||||
def _get_working_hours_per_calendar(self, is_uom_day=False):
|
||||
resource_calendar_per_hours = {}
|
||||
|
||||
if not is_uom_day:
|
||||
return resource_calendar_per_hours
|
||||
|
||||
read_group_data = self.env['resource.calendar']._read_group(
|
||||
[('id', 'in', self.employee_id.resource_calendar_id.ids)],
|
||||
['ids:array_agg(id)', 'hours_per_day'],
|
||||
['hours_per_day'],
|
||||
)
|
||||
for res in read_group_data:
|
||||
for calendar_id in res.get('ids', []):
|
||||
resource_calendar_per_hours[calendar_id] = res.get('hours_per_day')
|
||||
|
||||
return resource_calendar_per_hours
|
||||
|
||||
@api.depends_context('company')
|
||||
@api.depends('cost', 'employee_id.resource_calendar_id')
|
||||
def _compute_display_cost(self):
|
||||
is_uom_day = self.env.ref('uom.product_uom_day') == self.env.company.timesheet_encode_uom_id
|
||||
resource_calendar_per_hours = self._get_working_hours_per_calendar(is_uom_day)
|
||||
|
||||
for map_line in self:
|
||||
if is_uom_day:
|
||||
map_line.display_cost = map_line.cost * resource_calendar_per_hours.get(map_line.employee_id.resource_calendar_id.id, 1)
|
||||
else:
|
||||
map_line.display_cost = map_line.cost
|
||||
|
||||
def _inverse_display_cost(self):
|
||||
is_uom_day = self.env.ref('uom.product_uom_day') == self.env.company.timesheet_encode_uom_id
|
||||
resource_calendar_per_hours = self._get_working_hours_per_calendar(is_uom_day)
|
||||
|
||||
for map_line in self:
|
||||
if is_uom_day:
|
||||
map_line.cost = map_line.display_cost / resource_calendar_per_hours.get(map_line.employee_id.resource_calendar_id.id, 1)
|
||||
else:
|
||||
map_line.cost = map_line.display_cost
|
||||
|
||||
@api.depends('cost')
|
||||
def _compute_is_cost_changed(self):
|
||||
for map_entry in self:
|
||||
map_entry.is_cost_changed = map_entry.employee_id and map_entry.cost != map_entry.employee_id.hourly_cost
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
maps = super().create(vals_list)
|
||||
maps._update_project_timesheet()
|
||||
return maps
|
||||
|
||||
def write(self, values):
|
||||
res = super(ProjectProductEmployeeMap, self).write(values)
|
||||
self._update_project_timesheet()
|
||||
return res
|
||||
|
||||
def _update_project_timesheet(self):
|
||||
self.filtered(lambda l: l.sale_line_id).project_id._update_timesheets_sale_line_id()
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
from odoo.tools import float_utils, format_amount, formatLang
|
||||
from odoo.tools.misc import format_duration
|
||||
|
||||
|
||||
class ProjectUpdate(models.Model):
|
||||
_inherit = 'project.update'
|
||||
|
||||
@api.model
|
||||
def _get_template_values(self, project):
|
||||
template_values = super(ProjectUpdate, self)._get_template_values(project)
|
||||
services = self._get_services_values(project)
|
||||
profitability = self._get_profitability_values(project)
|
||||
show_sold = template_values['project'].allow_billable and len(services.get('data', [])) > 0
|
||||
return {
|
||||
**template_values,
|
||||
'show_sold': show_sold,
|
||||
'show_profitability': bool(profitability),
|
||||
'show_activities': template_values['show_activities'] or show_sold or bool(profitability),
|
||||
'services': services,
|
||||
'profitability': profitability,
|
||||
'format_value': lambda value, is_hour: str(round(value, 2)) if not is_hour else format_duration(value),
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _get_services_values(self, project):
|
||||
if not project.allow_billable:
|
||||
return {}
|
||||
|
||||
services = []
|
||||
total_sold, total_effective, total_remaining = 0, 0, 0
|
||||
sols = self.env['sale.order.line'].search(
|
||||
project._get_sale_items_domain([
|
||||
('is_downpayment', '=', False),
|
||||
]),
|
||||
)
|
||||
name_by_sol = dict(sols.with_context(with_price_unit=True).name_get())
|
||||
product_uom_unit = self.env.ref('uom.product_uom_unit')
|
||||
product_uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
company_uom = self.env.company.timesheet_encode_uom_id
|
||||
for sol in sols:
|
||||
#We only want to consider hours and days for this calculation
|
||||
is_unit = sol.product_uom == product_uom_unit
|
||||
if sol.product_uom.category_id == company_uom.category_id or is_unit:
|
||||
product_uom_qty = sol.product_uom._compute_quantity(sol.product_uom_qty, company_uom, raise_if_failure=False)
|
||||
qty_delivered = sol.product_uom._compute_quantity(sol.qty_delivered, company_uom, raise_if_failure=False)
|
||||
unit = sol.product_uom if is_unit else company_uom
|
||||
services.append({
|
||||
'name': name_by_sol[sol.id],
|
||||
'sold_value': product_uom_qty,
|
||||
'effective_value': qty_delivered,
|
||||
'remaining_value': product_uom_qty - qty_delivered,
|
||||
'unit': unit.name,
|
||||
'is_unit': is_unit,
|
||||
'is_hour': unit == product_uom_hour,
|
||||
'sol': sol,
|
||||
})
|
||||
if sol.product_uom.category_id == company_uom.category_id:
|
||||
total_sold += product_uom_qty
|
||||
total_effective += qty_delivered
|
||||
total_remaining = total_sold - total_effective
|
||||
|
||||
return {
|
||||
'data': services,
|
||||
'total_sold': total_sold,
|
||||
'total_effective': total_effective,
|
||||
'total_remaining': total_remaining,
|
||||
'company_unit_name': company_uom.name,
|
||||
'is_hour': company_uom == product_uom_hour,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _get_profitability_values(self, project):
|
||||
costs_revenues = project.analytic_account_id and project.allow_billable
|
||||
if not (self.user_has_groups('project.group_project_manager') and costs_revenues):
|
||||
return {}
|
||||
profitability_items = project._get_profitability_items(False)
|
||||
costs = sum(profitability_items['costs']['total'].values())
|
||||
revenues = sum(profitability_items['revenues']['total'].values())
|
||||
margin = revenues + costs
|
||||
return {
|
||||
'analytic_account_id': project.analytic_account_id,
|
||||
'costs': costs,
|
||||
'costs_formatted': format_amount(self.env, -costs, project.currency_id),
|
||||
'revenues': revenues,
|
||||
'revenues_formatted': format_amount(self.env, revenues, project.currency_id),
|
||||
'margin': margin,
|
||||
'margin_formatted': format_amount(self.env, margin, project.currency_id),
|
||||
'margin_percentage': formatLang(self.env,
|
||||
not float_utils.float_is_zero(costs, precision_digits=2) and (margin / -costs) * 100 or 0.0,
|
||||
digits=0),
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
invoice_policy = fields.Boolean(string="Invoice Policy", help="Timesheets taken when invoicing time spent")
|
||||
|
|
@ -0,0 +1,320 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import math
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import float_compare, format_duration
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
timesheet_ids = fields.Many2many('account.analytic.line', compute='_compute_timesheet_ids', string='Timesheet activities associated to this sale')
|
||||
timesheet_count = fields.Float(string='Timesheet activities', compute='_compute_timesheet_ids', groups="hr_timesheet.group_hr_timesheet_user")
|
||||
|
||||
# override domain
|
||||
project_id = fields.Many2one(domain="[('pricing_type', '!=', 'employee_rate'), ('analytic_account_id', '!=', False), ('company_id', '=', company_id)]")
|
||||
timesheet_encode_uom_id = fields.Many2one('uom.uom', related='company_id.timesheet_encode_uom_id')
|
||||
timesheet_total_duration = fields.Integer("Timesheet Total Duration", compute='_compute_timesheet_total_duration', help="Total recorded duration, expressed in the encoding UoM, and rounded to the unit")
|
||||
|
||||
def _compute_timesheet_ids(self):
|
||||
timesheet_groups = self.env['account.analytic.line'].sudo().read_group(
|
||||
[('so_line', 'in', self.mapped('order_line').ids), ('project_id', '!=', False)],
|
||||
['so_line', 'ids:array_agg(id)'],
|
||||
['so_line'])
|
||||
timesheets_per_sol = {group['so_line'][0]: (group['ids'], group['so_line_count']) for group in timesheet_groups}
|
||||
|
||||
for order in self:
|
||||
timesheet_ids = []
|
||||
timesheet_count = 0
|
||||
for sale_line_id in order.order_line.filtered('is_service').ids:
|
||||
list_timesheet_ids, count = timesheets_per_sol.get(sale_line_id, ([], 0))
|
||||
timesheet_ids.extend(list_timesheet_ids)
|
||||
timesheet_count += count
|
||||
|
||||
order.update({
|
||||
'timesheet_ids': self.env['account.analytic.line'].browse(timesheet_ids),
|
||||
'timesheet_count': timesheet_count,
|
||||
})
|
||||
|
||||
@api.depends('company_id.project_time_mode_id', 'timesheet_ids', 'company_id.timesheet_encode_uom_id')
|
||||
def _compute_timesheet_total_duration(self):
|
||||
if not self.user_has_groups('hr_timesheet.group_hr_timesheet_user'):
|
||||
self.update({'timesheet_total_duration': 0})
|
||||
return
|
||||
group_data = self.env['account.analytic.line'].sudo()._read_group([
|
||||
('order_id', 'in', self.ids), ('project_id', '!=', False)
|
||||
], ['order_id', 'unit_amount'], ['order_id'])
|
||||
timesheet_unit_amount_dict = defaultdict(float)
|
||||
timesheet_unit_amount_dict.update({data['order_id'][0]: data['unit_amount'] for data in group_data})
|
||||
for sale_order in self:
|
||||
total_time = sale_order.company_id.project_time_mode_id._compute_quantity(
|
||||
timesheet_unit_amount_dict[sale_order.id],
|
||||
sale_order.timesheet_encode_uom_id,
|
||||
rounding_method='HALF-UP',
|
||||
)
|
||||
sale_order.timesheet_total_duration = round(total_time)
|
||||
|
||||
def _compute_field_value(self, field):
|
||||
if field.name != 'invoice_status' or self.env.context.get('mail_activity_automation_skip'):
|
||||
return super()._compute_field_value(field)
|
||||
|
||||
# Get SOs which their state is not equal to upselling and if at least a SOL has warning prepaid service upsell set to True and the warning has not already been displayed
|
||||
upsellable_orders = self.filtered(lambda so:
|
||||
so.state == 'sale'
|
||||
and so.invoice_status != 'upselling'
|
||||
and so.id
|
||||
and (so.user_id or so.partner_id.user_id) # salesperson needed to assign upsell activity
|
||||
)
|
||||
super(SaleOrder, upsellable_orders.with_context(mail_activity_automation_skip=True))._compute_field_value(field)
|
||||
for order in upsellable_orders:
|
||||
upsellable_lines = order._get_prepaid_service_lines_to_upsell()
|
||||
if upsellable_lines:
|
||||
order._create_upsell_activity()
|
||||
# We want to display only one time the warning for each SOL
|
||||
upsellable_lines.write({'has_displayed_warning_upsell': True})
|
||||
super(SaleOrder, self - upsellable_orders)._compute_field_value(field)
|
||||
|
||||
def _get_prepaid_service_lines_to_upsell(self):
|
||||
""" Retrieve all sols which need to display an upsell activity warning in the SO
|
||||
|
||||
These SOLs should contain a product which has:
|
||||
- type="service",
|
||||
- service_policy="ordered_prepaid",
|
||||
"""
|
||||
self.ensure_one()
|
||||
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
return self.order_line.filtered(lambda sol:
|
||||
sol.is_service
|
||||
and not sol.has_displayed_warning_upsell # we don't want to display many times the warning each time we timesheet on the SOL
|
||||
and sol.product_id.service_policy == 'ordered_prepaid'
|
||||
and float_compare(
|
||||
sol.qty_delivered,
|
||||
sol.product_uom_qty * (sol.product_id.service_upsell_threshold or 1.0),
|
||||
precision_digits=precision
|
||||
) > 0
|
||||
)
|
||||
|
||||
def action_view_timesheet(self):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("sale_timesheet.timesheet_action_from_sales_order")
|
||||
action['context'] = {
|
||||
'search_default_billable_timesheet': True
|
||||
} # erase default filters
|
||||
if self.order_line:
|
||||
tasks = self.order_line.task_id._filter_access_rules_python('write')
|
||||
if tasks:
|
||||
action['context']['default_task_id'] = tasks[0].id
|
||||
else:
|
||||
projects = self.order_line.project_id._filter_access_rules_python('write')
|
||||
if projects:
|
||||
action['context']['default_project_id'] = projects[0].id
|
||||
if self.timesheet_count > 0:
|
||||
action['domain'] = [('so_line', 'in', self.order_line.ids), ('project_id', '!=', False)]
|
||||
else:
|
||||
action = {'type': 'ir.actions.act_window_close'}
|
||||
return action
|
||||
|
||||
def _create_invoices(self, grouped=False, final=False, date=None):
|
||||
"""Link timesheets to the created invoices. Date interval is injected in the
|
||||
context in sale_make_invoice_advance_inv wizard.
|
||||
"""
|
||||
moves = super()._create_invoices(grouped=grouped, final=final, date=date)
|
||||
moves._link_timesheets_to_invoice(self.env.context.get("timesheet_start_date"), self.env.context.get("timesheet_end_date"))
|
||||
return moves
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
|
||||
qty_delivered_method = fields.Selection(selection_add=[('timesheet', 'Timesheets')])
|
||||
analytic_line_ids = fields.One2many(domain=[('project_id', '=', False)]) # only analytic lines, not timesheets (since this field determine if SO line came from expense)
|
||||
remaining_hours_available = fields.Boolean(compute='_compute_remaining_hours_available', compute_sudo=True)
|
||||
remaining_hours = fields.Float('Remaining Hours on SO', compute='_compute_remaining_hours', compute_sudo=True, store=True)
|
||||
has_displayed_warning_upsell = fields.Boolean('Has Displayed Warning Upsell', copy=False)
|
||||
timesheet_ids = fields.One2many('account.analytic.line', 'so_line', domain=[('project_id', '!=', False)], string='Timesheets')
|
||||
|
||||
def name_get(self):
|
||||
res = super(SaleOrderLine, self).name_get()
|
||||
with_remaining_hours = self.env.context.get('with_remaining_hours')
|
||||
if with_remaining_hours:
|
||||
names = dict(res)
|
||||
result = []
|
||||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
uom_day = self.env.ref('uom.product_uom_day')
|
||||
for line in self:
|
||||
name = names.get(line.id)
|
||||
if line.remaining_hours_available:
|
||||
company = self.env.company
|
||||
encoding_uom = company.timesheet_encode_uom_id
|
||||
remaining_time = ''
|
||||
if encoding_uom == uom_hour:
|
||||
remaining_time = f' ({format_duration(line.remaining_hours)})'
|
||||
elif encoding_uom == uom_day:
|
||||
remaining_days = company.project_time_mode_id._compute_quantity(line.remaining_hours, encoding_uom, round=False)
|
||||
remaining_time = ' ({qty:.02f} {unit})'.format(
|
||||
qty=remaining_days,
|
||||
unit=_('days') if abs(remaining_days) > 1 else _('day')
|
||||
)
|
||||
name = '{name}{remaining_time}'.format(
|
||||
name=name,
|
||||
remaining_time=remaining_time
|
||||
)
|
||||
result.append((line.id, name))
|
||||
return result
|
||||
return res
|
||||
|
||||
@api.depends('product_id.service_policy')
|
||||
def _compute_remaining_hours_available(self):
|
||||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
for line in self:
|
||||
is_ordered_prepaid = line.product_id.service_policy == 'ordered_prepaid'
|
||||
is_time_product = line.product_uom.category_id == uom_hour.category_id
|
||||
line.remaining_hours_available = is_ordered_prepaid and is_time_product
|
||||
|
||||
@api.depends('qty_delivered', 'product_uom_qty', 'analytic_line_ids')
|
||||
def _compute_remaining_hours(self):
|
||||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
for line in self:
|
||||
remaining_hours = None
|
||||
if line.remaining_hours_available:
|
||||
qty_left = line.product_uom_qty - line.qty_delivered
|
||||
remaining_hours = line.product_uom._compute_quantity(qty_left, uom_hour)
|
||||
line.remaining_hours = remaining_hours
|
||||
|
||||
@api.depends('product_id')
|
||||
def _compute_qty_delivered_method(self):
|
||||
""" Sale Timesheet module compute delivered qty for product [('type', 'in', ['service']), ('service_type', '=', 'timesheet')] """
|
||||
super(SaleOrderLine, self)._compute_qty_delivered_method()
|
||||
for line in self:
|
||||
if not line.is_expense and line.product_id.type == 'service' and line.product_id.service_type == 'timesheet':
|
||||
line.qty_delivered_method = 'timesheet'
|
||||
|
||||
@api.depends('analytic_line_ids.project_id', 'project_id.pricing_type')
|
||||
def _compute_qty_delivered(self):
|
||||
super(SaleOrderLine, self)._compute_qty_delivered()
|
||||
|
||||
lines_by_timesheet = self.filtered(lambda sol: sol.qty_delivered_method == 'timesheet')
|
||||
domain = lines_by_timesheet._timesheet_compute_delivered_quantity_domain()
|
||||
mapping = lines_by_timesheet.sudo()._get_delivered_quantity_by_analytic(domain)
|
||||
for line in lines_by_timesheet:
|
||||
line.qty_delivered = mapping.get(line.id or line._origin.id, 0.0)
|
||||
|
||||
def _timesheet_compute_delivered_quantity_domain(self):
|
||||
""" Hook for validated timesheet in addionnal module """
|
||||
domain = [('project_id', '!=', False)]
|
||||
if self._context.get('accrual_entry_date'):
|
||||
domain += [('date', '<=', self._context['accrual_entry_date'])]
|
||||
return domain
|
||||
|
||||
###########################################
|
||||
# Service : Project and task generation
|
||||
###########################################
|
||||
|
||||
def _convert_qty_company_hours(self, dest_company):
|
||||
company_time_uom_id = dest_company.project_time_mode_id
|
||||
planned_hours = 0.0
|
||||
product_uom = self.product_uom
|
||||
if product_uom == self.env.ref('uom.product_uom_unit'):
|
||||
product_uom = self.env.ref('uom.product_uom_hour')
|
||||
if product_uom.category_id == company_time_uom_id.category_id:
|
||||
if product_uom != company_time_uom_id:
|
||||
planned_hours = product_uom._compute_quantity(self.product_uom_qty, company_time_uom_id)
|
||||
else:
|
||||
planned_hours = self.product_uom_qty
|
||||
return planned_hours
|
||||
|
||||
def _timesheet_create_project(self):
|
||||
project = super()._timesheet_create_project()
|
||||
project_uom = self.company_id.project_time_mode_id
|
||||
uom_unit = self.env.ref('uom.product_uom_unit')
|
||||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
|
||||
# dict of inverse factors for each relevant UoM found in SO
|
||||
factor_inv_per_id = {
|
||||
uom.id: uom.factor_inv
|
||||
for uom in self.order_id.order_line.product_uom
|
||||
if uom.category_id == project_uom.category_id
|
||||
}
|
||||
# if sold as units, assume hours for time allocation
|
||||
factor_inv_per_id[uom_unit.id] = uom_hour.factor_inv
|
||||
|
||||
allocated_hours = 0.0
|
||||
# method only called once per project, so also allocate hours for
|
||||
# all lines in SO that will share the same project
|
||||
for line in self.order_id.order_line:
|
||||
if line.is_service \
|
||||
and line.product_id.service_tracking in ['task_in_project', 'project_only'] \
|
||||
and line.product_id.project_template_id == self.product_id.project_template_id \
|
||||
and line.product_uom.id in factor_inv_per_id:
|
||||
uom_factor = project_uom.factor * factor_inv_per_id[line.product_uom.id]
|
||||
allocated_hours += line.product_uom_qty * uom_factor
|
||||
|
||||
project.write({
|
||||
'allocated_hours': allocated_hours,
|
||||
'allow_timesheets': True,
|
||||
})
|
||||
return project
|
||||
|
||||
def _timesheet_create_project_prepare_values(self):
|
||||
"""Generate project values"""
|
||||
values = super()._timesheet_create_project_prepare_values()
|
||||
values['allow_billable'] = True
|
||||
return values
|
||||
|
||||
def _recompute_qty_to_invoice(self, start_date, end_date):
|
||||
""" Recompute the qty_to_invoice field for product containing timesheets
|
||||
|
||||
Search the existed timesheets between the given period in parameter.
|
||||
Retrieve the unit_amount of this timesheet and then recompute
|
||||
the qty_to_invoice for each current product.
|
||||
|
||||
:param start_date: the start date of the period
|
||||
:param end_date: the end date of the period
|
||||
"""
|
||||
lines_by_timesheet = self.filtered(lambda sol: sol.product_id and sol.product_id._is_delivered_timesheet())
|
||||
domain = lines_by_timesheet._timesheet_compute_delivered_quantity_domain()
|
||||
refund_account_moves = self.order_id.invoice_ids.filtered(lambda am: am.state == 'posted' and am.move_type == 'out_refund').reversed_entry_id
|
||||
timesheet_domain = [
|
||||
'|',
|
||||
('timesheet_invoice_id', '=', False),
|
||||
('timesheet_invoice_id.state', '=', 'cancel')]
|
||||
if refund_account_moves:
|
||||
credited_timesheet_domain = [('timesheet_invoice_id.state', '=', 'posted'), ('timesheet_invoice_id', 'in', refund_account_moves.ids)]
|
||||
timesheet_domain = expression.OR([timesheet_domain, credited_timesheet_domain])
|
||||
domain = expression.AND([domain, timesheet_domain])
|
||||
if start_date:
|
||||
domain = expression.AND([domain, [('date', '>=', start_date)]])
|
||||
if end_date:
|
||||
domain = expression.AND([domain, [('date', '<=', end_date)]])
|
||||
mapping = lines_by_timesheet.sudo()._get_delivered_quantity_by_analytic(domain)
|
||||
|
||||
for line in lines_by_timesheet:
|
||||
qty_to_invoice = mapping.get(line.id, 0.0)
|
||||
if qty_to_invoice:
|
||||
line.qty_to_invoice = qty_to_invoice
|
||||
else:
|
||||
prev_inv_status = line.invoice_status
|
||||
line.qty_to_invoice = qty_to_invoice
|
||||
line.invoice_status = prev_inv_status
|
||||
|
||||
def _get_action_per_item(self):
|
||||
""" Get action per Sales Order Item
|
||||
|
||||
When the Sales Order Item contains a service product then the action will be View Timesheets.
|
||||
|
||||
:returns: Dict containing id of SOL as key and the action as value
|
||||
"""
|
||||
action_per_sol = super()._get_action_per_item()
|
||||
timesheet_action = self.env.ref('sale_timesheet.timesheet_action_from_sales_order_item').id
|
||||
timesheet_ids_per_sol = {}
|
||||
if self.user_has_groups('hr_timesheet.group_hr_timesheet_user'):
|
||||
timesheet_read_group = self.env['account.analytic.line']._read_group([('so_line', 'in', self.ids), ('project_id', '!=', False)], ['so_line', 'ids:array_agg(id)'], ['so_line'])
|
||||
timesheet_ids_per_sol = {res['so_line'][0]: res['ids'] for res in timesheet_read_group}
|
||||
for sol in self:
|
||||
timesheet_ids = timesheet_ids_per_sol.get(sol.id, [])
|
||||
if sol.is_service and len(timesheet_ids) > 0:
|
||||
action_per_sol[sol.id] = timesheet_action, timesheet_ids[0] if len(timesheet_ids) == 1 else False
|
||||
return action_per_sol
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import timesheets_analysis_report
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="timesheet_sale_page">
|
||||
<t t-set="show_project" t-value="true"/>
|
||||
<t t-set="show_task" t-value="true"/>
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
<t t-set="doc_name" t-value="doc.name"/>
|
||||
<t t-if="with_order_id" t-set="doc_name" t-value="str(doc.order_id.name) +' - '+ str(doc_name)"/>
|
||||
<t t-elif="doc_name == '/'" t-set="doc_name" t-value="'Draft'"/>
|
||||
<div class="oe_structure"/>
|
||||
<div class="row mt8">
|
||||
<div class="col-12">
|
||||
<t t-if="doc.timesheet_ids">
|
||||
<h2>
|
||||
<br/>
|
||||
<span>Timesheets for the <t t-out="doc_name"/> <t t-out="record_name"/>
|
||||
</span>
|
||||
</h2>
|
||||
<t t-set='lines' t-value='doc.timesheet_ids'/>
|
||||
<t t-call="hr_timesheet.timesheet_table"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Sale Order Timesheet Report for given timesheets -->
|
||||
<template id="report_timesheet_sale_order">
|
||||
<t t-set="record_name">Sales Order Item</t>
|
||||
<t t-set="with_order_id" t-value="true"/>
|
||||
<t t-set="docs" t-value="docs.order_line"/>
|
||||
<t t-call="sale_timesheet.timesheet_sale_page"/>
|
||||
</template>
|
||||
|
||||
<record id="timesheet_report_sale_order" model="ir.actions.report">
|
||||
<field name="name">Timesheets</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">sale_timesheet.report_timesheet_sale_order</field>
|
||||
<field name="binding_model_id" ref="model_sale_order"/>
|
||||
</record>
|
||||
|
||||
<!-- Invoice Timesheet Report for given timesheets -->
|
||||
<template id="report_timesheet_account_move">
|
||||
<t t-set="record_name">Invoice</t>
|
||||
<t t-call="sale_timesheet.timesheet_sale_page"/>
|
||||
</template>
|
||||
|
||||
<record id="timesheet_report_account_move" model="ir.actions.report">
|
||||
<field name="name">Timesheets</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">sale_timesheet.report_timesheet_account_move</field>
|
||||
<field name="binding_model_id" ref="model_account_move"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, api
|
||||
|
||||
from odoo.addons.sale_timesheet.models.account import TIMESHEET_INVOICE_TYPES
|
||||
|
||||
|
||||
class TimesheetsAnalysisReport(models.Model):
|
||||
_inherit = "timesheets.analysis.report"
|
||||
|
||||
order_id = fields.Many2one("sale.order", string="Sales Order", readonly=True)
|
||||
so_line = fields.Many2one("sale.order.line", string="Sales Order Item", readonly=True)
|
||||
timesheet_invoice_type = fields.Selection(TIMESHEET_INVOICE_TYPES, string="Billable Type", readonly=True)
|
||||
timesheet_invoice_id = fields.Many2one("account.move", string="Invoice", readonly=True, help="Invoice created from the timesheet")
|
||||
timesheet_revenues = fields.Float("Timesheet Revenues", readonly=True, help="Number of hours spent multiplied by the unit price per hour/day.")
|
||||
margin = fields.Float("Margin", readonly=True, help="Timesheets revenues minus the costs")
|
||||
billable_time = fields.Float("Billable Hours", readonly=True, help="Number of hours/days linked to a SOL.")
|
||||
non_billable_time = fields.Float("Non-billable Hours", readonly=True, help="Number of hours/days not linked to a SOL.")
|
||||
|
||||
@property
|
||||
def _table_query(self):
|
||||
return """
|
||||
SELECT A.*,
|
||||
(timesheet_revenues + A.amount) AS margin,
|
||||
(A.unit_amount - billable_time) AS non_billable_time
|
||||
FROM (
|
||||
%s %s %s
|
||||
) A
|
||||
""" % (self._select(), self._from(), self._where())
|
||||
|
||||
@api.model
|
||||
def _select(self):
|
||||
return super()._select() + """,
|
||||
A.order_id AS order_id,
|
||||
A.so_line AS so_line,
|
||||
A.timesheet_invoice_type AS timesheet_invoice_type,
|
||||
A.timesheet_invoice_id AS timesheet_invoice_id,
|
||||
CASE WHEN A.order_id IS NULL THEN 0 ELSE A.unit_amount * SOL.price_unit * sol_product_uom.factor / a_product_uom.factor END AS timesheet_revenues,
|
||||
CASE WHEN A.order_id IS NULL THEN 0 ELSE A.unit_amount END AS billable_time
|
||||
"""
|
||||
|
||||
@api.model
|
||||
def _from(self):
|
||||
return super()._from() + """
|
||||
LEFT JOIN sale_order_line SOL ON A.so_line = SOL.id
|
||||
LEFT JOIN uom_uom sol_product_uom ON sol_product_uom.id=SOL.product_uom
|
||||
INNER JOIN uom_uom a_product_uom ON a_product_uom.id=A.product_uom_id"""
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="timesheets_analysis_report_pivot_inherit" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.pivot</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_pivot_employee"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='unit_amount']" position="after">
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="timesheets_analysis_report_graph_inherit" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.graph</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_pivot_employee"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='unit_amount']" position="after">
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--TO DO: Remove in master and update existing inherit_id-->
|
||||
<record id="timesheets_analysis_report_graph_timesheet_grid" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.graph</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_graph_employee"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='unit_amount']" position="after">
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="timesheets_analysis_report_pivot_project_inherit" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.pivot.project</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_pivot_project"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='unit_amount']" position="after">
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="timesheets_analysis_report_graph_project_inherit" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.graph.project</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_graph_project"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='unit_amount']" position="after">
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="timesheets_analysis_report_pivot_task_inherit" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.pivot.task</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_pivot_task"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='unit_amount']" position="after">
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="timesheets_analysis_report_graph_task_inherit" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.graph.task</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_graph_task"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='unit_amount']" position="after">
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="timesheets_analysis_report_pivot_invoice_type" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.pivot</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Timesheets Analysis" sample="1" disable_linking="True">
|
||||
<field name="date" interval="month" type="row"/>
|
||||
<field name="timesheet_invoice_type" type="col"/>
|
||||
<field name="amount" string="Timesheet Costs"/>
|
||||
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="timesheets_analysis_report_graph_invoice_type" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.graph</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview" disable_linking="True">
|
||||
<field name="amount" string="Timesheet Costs"/>
|
||||
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
|
||||
<field name="billable_time" widget="timesheet_uom"/>
|
||||
<field name="non_billable_time" widget="timesheet_uom"/>
|
||||
<field name="timesheet_invoice_type" type="row"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_timesheet_report_search_sale_timesheet" model="ir.ui.view">
|
||||
<field name="name">timesheets.analysis.report.search</field>
|
||||
<field name="model">timesheets.analysis.report</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_report_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='task_id']" position="after">
|
||||
<field name="order_id" string="Sales Order" groups="sales_team.group_sale_salesman"/>
|
||||
<field name="so_line" groups="sales_team.group_sale_salesman"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='month']" position="before">
|
||||
<filter name="billable_fixed" string="Billed at a Fixed Price" domain="[('timesheet_invoice_type', '=', 'billable_fixed')]"
|
||||
groups="sales_team.group_sale_salesman"/>
|
||||
<filter name="billable_time" string="Billed on Timesheets" domain="[('timesheet_invoice_type', '=', 'billable_time')]"
|
||||
groups="sales_team.group_sale_salesman"/>
|
||||
<filter name="billable_milestones" string="Billed on Milestones" domain="[('timesheet_invoice_type', '=', 'billable_milestones')]"
|
||||
groups="sales_team.group_sale_salesman"/>
|
||||
<filter name="billable_manual" string="Billed Manually" domain="[('timesheet_invoice_type', '=', 'billable_manual')]"
|
||||
groups="sales_team.group_sale_salesman"/>
|
||||
<filter name="non_billable" string="Non-Billable" domain="[('timesheet_invoice_type', '=', 'non_billable')]"
|
||||
groups="sales_team.group_sale_salesman"/>
|
||||
<separator/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='groupby_employee']" position="after">
|
||||
<filter string="Sales Order" name="groupby_sale_order" domain="[]"
|
||||
context="{'group_by': 'order_id'}" groups="sales_team.group_sale_salesman"/>
|
||||
<filter string="Sales Order Item" name="groupby_sale_order_item" domain="[]"
|
||||
context="{'group_by': 'so_line'}" groups="sales_team.group_sale_salesman"/>
|
||||
<filter string="Invoice" name="groupby_invoice" domain="[]"
|
||||
context="{'group_by': 'timesheet_invoice_id'}" groups="sales_team.group_sale_salesman"/>
|
||||
<filter string="Billable Type" name="groupby_timesheet_invoice_type" domain="[]"
|
||||
context="{'group_by': 'timesheet_invoice_type'}" groups="sales_team.group_sale_salesman"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="timesheet_action_billing_report" model="ir.actions.act_window">
|
||||
<field name="name">Timesheets by Billing Type</field>
|
||||
<field name="res_model">timesheets.analysis.report</field>
|
||||
<field name="domain">[('project_id', '!=', False)]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No data yet!
|
||||
</p>
|
||||
<p>Review your timesheets by billing type and make sure your time is billable.</p>
|
||||
</field>
|
||||
<field name="search_view_id" ref="hr_timesheet.hr_timesheet_report_search"/>
|
||||
<field name="view_mode">pivot,graph</field>
|
||||
</record>
|
||||
|
||||
<record id="timesheet_action_view_report_by_billing_rate_pivot" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="5"/>
|
||||
<field name="view_mode">pivot</field>
|
||||
<field name="view_id" ref="timesheets_analysis_report_pivot_invoice_type"/>
|
||||
<field name="act_window_id" ref="timesheet_action_billing_report"/>
|
||||
</record>
|
||||
|
||||
<record id="timesheet_action_view_report_by_billing_rate_graph" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="6"/>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="view_id" ref="timesheets_analysis_report_graph_invoice_type"/>
|
||||
<field name="act_window_id" ref="timesheet_action_billing_report"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_timesheet_billing_analysis"
|
||||
parent="hr_timesheet.menu_timesheets_reports_timesheet"
|
||||
action="timesheet_action_billing_report"
|
||||
name="By Billing Type"
|
||||
sequence="40"/>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_project_sale_line_employee_map,access_project_sale_line_employee_map,model_project_sale_line_employee_map,base.group_user,1,0,0,0
|
||||
access_project_sale_line_employee_map_manager,access_project_sale_line_employee_map_project_manager,model_project_sale_line_employee_map,project.group_project_manager,1,1,1,1
|
||||
access_project_create_sale_order,access.project.create.sale.order,model_project_create_sale_order,sales_team.group_sale_salesman,1,1,1,0
|
||||
access_project_create_sale_order_line,access.project.create.sale.order.line,model_project_create_sale_order_line,sales_team.group_sale_salesman,1,1,1,1
|
||||
access_project_create_invoice,access.project.create.invoice,model_project_create_invoice,sales_team.group_sale_salesman_all_leads,1,1,1,0
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue