diff --git a/README.md b/README.md index 8b10e6960..6864725ce 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# odex25-standard-moduless +# odex25-standard-modules This Repo contains general standard modules for all projects. diff --git a/odex25_project/kpi_scorecard_odex/__init__.py b/odex25_project/kpi_scorecard_odex/__init__.py new file mode 100644 index 000000000..30a7de2e7 --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- + +from . import models +from . import reports + + diff --git a/odex25_project/kpi_scorecard_odex/__manifest__.py b/odex25_project/kpi_scorecard_odex/__manifest__.py new file mode 100644 index 000000000..a200accce --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/__manifest__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# (Odex - Extending the base module). +# Copyright (C) 2024 Expert Co. Ltd. (). +# +############################################################################## +{ + "name": "Odex -KPI", + "version": "1.0", + "author": "Expert Co. Ltd.", + "category": "Odex25-base/Odex-Base25", + "website": "http://www.exp-sa.com", + "installable": True, + "depends": [ + "kpi_scorecard", + + ], + "data": [ + "security/ir.model.access.csv", + "views/kpi_category.xml", + "views/kpi_item.xml", + "views/kpi_scorecard_line.xml", + 'views/menu_security_cus.xml' + + + + + + + ], + + + +} diff --git a/odex25_project/kpi_scorecard_odex/i18n/ar_001.po b/odex25_project/kpi_scorecard_odex/i18n/ar_001.po new file mode 100644 index 000000000..7f9f1e52e --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/i18n/ar_001.po @@ -0,0 +1,1999 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * kpi_scorecard +# * kpi_scorecard_odex +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-03-03 00:57+0000\n" +"PO-Revision-Date: 2024-03-03 00:57+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: kpi_scorecard +#: model:ir.model.fields,help:kpi_scorecard.field_kpi_measure__date_field_ids +msgid "" +"\n" +" According to those dates Odoo will calculate whether a record is within a specified period.\n" +" In case there are a few of date fields, all of related dates should be within a period. \n" +" " +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_help.py:0 +#, python-format +msgid "" +"\n" +"
\n" +"

KPI categories serve to structure KPIs and KPI targets for comfortable navigation. Each\n" +"KPI should be assigned for a single category, what allows users to find required targets quickly just by checking the\n" +"boxes on the scorecard interface.

\n" +"

Hierarchy of categories let users also combine targets in sections to control KPIs\n" +"related to specific areas. For example, to check targets only in sales (e.g. category 'sales') or targets of a \n" +"specific sales team (e.g. category 'sales > sales team Europe').

\n" +"

Moreover, KPI categories let you administrate user accesses in a batch. Thus, you may\n" +"grant users and/or user groups an access for this category KPI ('Read Rights') or a right to update those\n" +"targets ('Edit rights)'. Thus, there would be no need to manage each KPI separately. Take into account that those\n" +"settings are additive and are not restrictive, meaning that KPI managers would any way have full rights for all\n" +"KPIs.

\n" +"
\n" +" " +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_help.py:0 +#, python-format +msgid "" +"\n" +"
\n" +"

\n" +"A KPI constant is a variable type used for formula construction. In comparison to measurements, KPI constants are fixed\n" +"and they do not depend on actual Odoo data. This allows to introduce figures which can not be retrieved from modules\n" +"and/or which should remain the same during the whole period.

\n" +"

For example, you might set the 'total size of investments' to calculate return on \n" +"investments, or the 'number of salesmen' to get sales revenue per person.

\n" +"

KPI constant value might be defined for each individual period. If the value does not \n" +"exist for a calculated period, the app would try to check the parent time frame. For instance, if this constant value\n" +"does is not set up January 2021, the app would take all the intervals which include January (e.g. Quarter 1 2021 and the\n" +"year 2021 consequentially). In case the values is not defined for those periods as well, then, the global value would be \n" +"applied.

\n" +"
\n" +" " +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_help.py:0 +#, python-format +msgid "" +"\n" +"
\n" +"

A KPI measurement is a final variable used for KPI(s) calculations. It represents\n" +"specification of basic measurement. For example, 'number of quotations of the sales team Europe' might be a precision of\n" +"a basic measurement 'total number of sales orders'.

\n" +"

Such approach significantly simplifies variables' preparation, since each basic\n" +"measurement might have an unlimited number of linked KPI measurements. So, the only thing you would need to do is to\n" +"apply extra filters (in the example 'Sales Team Name is Europe').

\n" +"

Take into account that basic measurements of the type 'Execute Python code' can't be\n" +"any more specified, since they do not relate to any records. In such a case, there is no sense to have a few KPI\n" +"measurements.

\n" +"

In a multi company environment KPI Measurements are applied globally or for each company\n" +"individually. In the former case that variable is available for any company KPI formulas, while in the latter – only for\n" +" specific one (it let make quicker overview while constructing formulas).

\n" +"
\n" +" " +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_help.py:0 +#, python-format +msgid "" +"\n" +"
\n" +"

A KPI period is a time frame for which companies set their targets to control \n" +"performance (in a similar way as financial performance is controlled within accounting/fiscal periods).

\n" +"

KPI scorecards in comparison to simple dashboard assume that you might not only have \n" +"overview of actual figures, but to compare those to real targets. However, goals should be time-constrained ('Sell as \n" +"much as possible' is not a goal, while 'Generate 100,000 Euro Revenue in the year 2021' is a good target for a sales \n" +"person, for example). That is why KPI periods are introduced.

\n" +"

The app allows to set periods of any length including intervals which cross each other \n" +"(and even the Past periods!).\n" +"To open a new KPI period you should just set start and end dates. Those dates will be used to calculate KPI actual value.\n" +"For example, KPI formula might include a basic KPI measurement for total amount of sale orders, while date field of \n" +"this measurement is defined as 'Order date'. Then, only sale orders which order date is within the period would be \n" +"taken into account. Thus, KPI scorecard would show total revenue for a given period.

\n" +"

It is recommended to have more or less strict logic of periods for comparability \n" +"purposes. Usually, KPI targets are set for a whole year and quarterly/monthly intervals. It let not only have KPI \n" +"overview, but also check historical trends. The app automatically considers various periods in order to define which \n" +"might be compared to this one, and will show users a chart of actual values by a specific KPI. It is possible to define \n" +"tolerance on the configuration page. For example, 2-days tolerance is needed to compare quarterly periods (since quarter\n" +" might take from 90 to 92 days), and 3-days tolerance for months (unluckily, February may last 28 days).

\n" +"

When end date is already in the Past, it is preferable to close the period to avoid \n" +"further updates of KPI targets actual values due to further corrections (it is not a good idea to update December KPIs \n" +"in the next August even though a sale total needs to be corrected, for example). This action is pretty much the same \n" +"as when accountant finishes a fiscal year. To close a period use the button 'Close period' (this action might be rolled\n" +"back by re-opening the period).

\n" +"

KPI periods let you also copy targets from existing periods (so, use them as a \n" +"template). To that end just push the button 'Substitute targets' and select a period with proper KPI targets. \n" +"It significantly saves time, since KPI targets usually remain similar for periods of the same length. Do not worry, \n" +"after that action you would be still able to change actual scorecard for this period (for sure, until a period is \n" +"closed).

\n" +"

Based on KPI periods you may also configure KPI formulas to rely upon period length in \n" +"days to calculate figures per days, weeks, etc. To that end use the special Measurements 'Periods Days' or 'Days Passed'\n" +"(how many days already passed in comparison to today) when you construct a formula. For example, you might have average \n" +"sales amount per week within the given period, and compare how much it changes from the previous one. That length is\n" +"calculated automatically, while you just put that to a proper formula place.

\n" +"
\n" +" " +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_help.py:0 +#, python-format +msgid "" +"\n" +"
\n" +"

A basic measurement is the core object used for retrieving actual KPI value from Odoo \n" +"data. Although basic measurements are not used themselves for formula constructions, they are required to prepare any \n" +"sort of formula variables (KPI measurements).

\n" +"

A basic measurement represents the most general calculation, while KPI measurements \n" +"specify those. For example, 'total number of sales orders' should be a basic measurement, while narrower 'number of \n" +"quotations of the sales team Europe' is recommended to be a precision of that basic measurement (so, KPI measurement).\n" +"Each basic measurement might have an unlimited number of linked KPI measurements.

\n" +"

Calculation Types

\n" +"

Basic measurements assume a few types of low-level calculations:

\n" +"\n" +"

Basic Measurement Settings

\n" +"

The first 3 calculation types assume that you define how records should be searched and \n" +"which records fields should be used for computations.

\n" +"\n" +"

All settings might relate to your custom objects or custom fields, including ones\n" +"created through the interface or the Odoo studio.

\n" +"
\n" +" " +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_help.py:0 +#, python-format +msgid "" +"\n" +"
\n" +"

KPIs (key performance indicators) are measurements which aim to evaluate organizational\n" +"or personal success of a definite activity. Different companies have different KPIs depending on their strategy and\n" +"business area. However, all KPIs have the common core attribute: they must be measurable within a target period.

\n" +"

To that end KPIs allow to construct formulas to retrieve Odoo data sets and process\n" +"those to real figures. Formula preparation is as simple as it is to write down a mathematical expression: just drag and\n" +"drop formula parts in a right order with correct operators.

\n" +"

KPIs, KPI periods, and KPI targets

\n" +"

KPIs represent the list of success figures you may use to plan your company activities.\n" +"Simultaneously, measurements are almost senseless unless you have target values for those KPIs. That is possible only\n" +"within a time-constrained period. 'Sell as much as you can' is not a goal, while 'Generate 100,000 Euro Revenue in the\n" +"year 2021' is a good aim for a sales person, for example. To that end KPI periods are introduced.

\n" +"

A Combination of a KPI and a KPI period results in a KPI target. Exactly with those\n" +"targets you work on the score card interface. For each KPI you would like to manage in this period, you should define\n" +"a planned value to compare those to actual at the end of the period.

\n" +"

So, a KPI itself defines how to compute actual value for period and how to estimate the\n" +"result ('the more the better' or 'the less the better'), but does not assume setting targets. The latter should be done\n" +"for each period.

\n" +"

Formula parts

\n" +"

As variables for formula you may use:

\n" +"

Measurements (KPI measurements, KPI variables) are figures calculated from actual\n" +"Odoo data for a checked period. For example, 'number of quotations of the sales team Europe'.

\n" +"

Among measurements you may also find 2 very specific variables: 'Period Days' and 'Days\n" +"Passed'. Those figures are calculated not from Odoo data, but from the KPI period settings under consideration. 'Period\n" +"Days' is an interval length in days. 'Days Passed' is a length between period start and today (if today is before period\n" +"end; otherwise period end). Those parameters let calculate per-time KPIs, such as, for example, 'Average sales per \n" +"week'.

\n" +"

Constants (KPI constants) are fixed numbers applied globally or for a period.\n" +"Such numbers do not depend on Odoo data. So, they let define strict non-changeable figures which you can't otherwise get\n" +"from Odoo. For example, 'Total investments'.

\n" +"

Other KPIs are results of other KPI formula calculations. That variables allow to\n" +"construct derivative complex calculations and make up hierarchy. For example, you may have KPIs 'Sales count' and\n" +"'Opportunities count', and a derivative KPI 'Opportunity to sales success ratio'.

\n" +"

Variables of any types above might be added to a formula. You may drag and drop as many\n" +"variables as you like (and even use the same variable twice). Just do not forget to add operators in between to make\n" +"correct mathematical expressions. The following operators are available:

\n" +"\n" +"

Result appearance

\n" +"

Depending of business logic of KPI formula, the final calculation result might have \n" +"different form:

\n" +"\n" +"

For simple numbers and percentage you may also define result suffix and prefix to make a\n" +"figure nice looking (e.g. add \"%\" as suffix to have \"88%\"). For monetary result type it is recommended\n" +"to define a currency, which symbol would be added to result.

\n" +"

Finally, you may decide how calculation result should be rounded. Available options are\n" +"from 0 to 4 decimal points (1 > 1.2 > 1.23 > 1.235 > 1.2346).

\n" +"

Categories and hierarchy

\n" +"

To make navigation by KPI targets more comfortable, KPIs are combined into KPI\n" +"categories. It let not only quickly search targets inside a scorecard, but it let grant additive access rights. In such\n" +"a way, users would overview only their KPIs structured in sections.

\n" +"

Each KPI might also have a parent. Such hierarchy let organize KPI scorecard with\n" +"indicative padding. For example, 'Total company sales' might have children 'Sales Europe' and 'Sales America'. The\n" +"latter 2 might be further specified by sales persons.

\n" +"

Additive access to KPIs and targets

\n" +"

By default KPIs and there linked targets are available only for users with the right\n" +"'KPI Manager'. Simultaneously, you may grant extra rights for other user groups or/and definite users. To that end it is\n" +"possible to define 'Read Rights' and 'Edit Rights' on KPI category or KPI form views (take into account that KPI\n" +"category rights and KPI own rights are combined!).

\n" +"

Read rights define which user and user groups would be able to observe KPI targets on\n" +"the scorecard interface. For example, you might want to share tasks' targets by persons with related project users in\n" +"order their control themselves.

\n" +"

Edit rights assume sharing an access to set specific targets up. For instance, you may\n" +"find it a good idea to involve sales manager to set sub targets for their sales team.

\n" +"

Take into account: all security settings are additive and they are not restrictive. KPI\n" +"managers would have full rights for all KPIs disregarding those settings, while other users would have rights only to\n" +"KPIs which settings (or category settings) allow them so.

\n" +"
\n" +" " +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_help.py:0 +#, python-format +msgid "" +"\n" +"

A KPI target is your plan for this KPI for a given period. By setting up a target\n" +"value, you indicate which result you would like to achieve by the end of the period.

\n" +"

KPI targets actual values are re-calculated regularly (not in real time) and\n" +"automatically by the Odoo cron job. Alternatively, you may press the button 'Calculate' on the left navigation bar.

\n" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_help.py:0 +#, python-format +msgid "" +"\n" +"

The action removes all current targets and copies targets from a chosen period. After \n" +"that action you would be still able to modify scorecard: change targets' values, delete certain KPI targets, or add new\n" +"ones.

\n" +" " +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_kanban +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +msgid "#{record.computation_error.raw_value}" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_kanban +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +msgid "#{record.description.raw_value and record.description.raw_value or ''}" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__access_user_ids +msgid "Access Users" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__data_source__achievement +msgid "Achievement" +msgstr "انجاز" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__active +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__active +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__active +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__active +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__active +msgid "Active" +msgstr "نشط" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__edit_rights +msgid "Active User Editor" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#. openerp-web +#: code:addons/kpi_scorecard/models/kpi_period.py:0 +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__formatted_actual_value +#, python-format +msgid "Actual" +msgstr "المحقق" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__actual_value +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_kanban +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +msgid "Actual Value" +msgstr "المحقق" + +#. module: kpi_scorecard +#: model_terms:ir.actions.act_window,help:kpi_scorecard.kpi_period_action +msgid "Add a new period to set KPI targets" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.actions.act_window,help:kpi_scorecard.kpi_item_action +msgid "Add new KPI to measure performance" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.actions.act_window,help:kpi_scorecard.kpi_constant_action +msgid "Add new company constants which are used for KPI calculations" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.actions.act_window,help:kpi_scorecard.kpi_measure_action +msgid "" +"Add new low-level measurements which are used to distinguish KPI calculations variables.\n" +" For example, won opportunities count, total sales, etc." +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.actions.act_window,help:kpi_scorecard.kpi_measure_item_action +msgid "" +"Add new measurements (variables) to calculate KPIs.\n" +" For example, won opportunities count for sales team 'Europe', total sales conducted by John Brown, etc." +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__formula_warning +msgid "Alert" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__all_parent_ids +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__all_parents +msgid "All Parents" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__user_group_ids +msgid "Allowed User Groups" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__user_ids +msgid "Allowed Users" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__measurement_periodic__annually +msgid "Annually" +msgstr "سنوي" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_constant_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_item_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_search +msgid "Archived" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__automated_weight +msgid "Automated Weight" +msgstr "الوزن تلقائيا" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_measure__measure_type__average +msgid "Average of records field" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.actions.act_window,name:kpi_scorecard.kpi_measure_action +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_basic_measures +msgid "Basic Measurements" +msgstr "التكامل" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_constant_view_form +msgid "By Periods" +msgstr "بالفترة" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#, python-format +msgid "Calculate" +msgstr "حساب" + +#. module: kpi_scorecard +#: model:ir.actions.server,name:kpi_scorecard.cron_recalculate_kpi_periods_ir_actions_server +#: model:ir.cron,cron_name:kpi_scorecard.cron_recalculate_kpi_periods +#: model:ir.cron,name:kpi_scorecard.cron_recalculate_kpi_periods +msgid "Calculate KPIs" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_copy_template_wizard_form_view +msgid "Cancel" +msgstr "الغاء" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#, python-format +msgid "Categories" +msgstr "الاهداف" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__category_id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__category_id +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_search +msgid "Category" +msgstr "الاهداف" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_kanban +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +msgid "Change Target" +msgstr "تغيير المستهدف" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__child_ids +msgid "Child Categories" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__child_ids +msgid "Child KPIs" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +#, python-format +msgid "Close Period" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_period__state__closed +msgid "Closed" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_category__code +msgid "Code" +msgstr "الكود" + +#. module: kpi_scorecard +#: model:ir.model,name:kpi_scorecard.model_res_company +msgid "Companies" +msgstr "شركات" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__company_id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__company_id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__company_id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__company_id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__company_id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__company_id +msgid "Company" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__company_field_id +msgid "Company Field" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__company_field_name +msgid "Company Field Name" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_config_settings__kpi_company_id +msgid "Company for KPI settings" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_measure_item.py:0 +#, python-format +msgid "" +"Computation Error: KPI is not correctly defined. Perhaps, the related module was uninstalled or \n" +" field was removed. Please check basic measurements involved in formula" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_measure.py:0 +#, python-format +msgid "" +"Computation Error: Python code is incorrect: it doesn't contain 'result' key" +" word" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_measure.py:0 +#, python-format +msgid "" +"Computation Error: Python code is incorrect: it returns not number but {}" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "Computation Error: the formula is empty" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#: code:addons/kpi_scorecard/models/kpi_measure.py:0 +#, python-format +msgid "Computation Error: {}" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_search +msgid "Computation Errors" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model,name:kpi_scorecard.model_res_config_settings +msgid "Config Settings" +msgstr "ضبط الاعدادات" + +#. module: kpi_scorecard +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_conf +msgid "Configuration" +msgstr "الاعدادات" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.res_config_settings_view_form +msgid "Configure cron job" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.res_config_settings_view_form +msgid "" +"Configure when and how frequent KPIs actual values should be re-calculated" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#: model:ir.actions.act_window,name:kpi_scorecard.kpi_constant_action +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__constant_ids +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_constants +#, python-format +msgid "Constants" +msgstr "ادخال القياس" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__template_id +msgid "Copy targets from" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_measure__measure_type__count +msgid "Count of records" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__create_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__create_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__create_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__create_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__create_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__create_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__create_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__create_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__create_uid +msgid "Created by" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__create_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__create_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__create_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__create_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__create_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__create_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__create_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__create_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__create_date +msgid "Created on" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__currency_id +msgid "Currency" +msgstr "العملة" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__measurement_method__data_entry +msgid "Data entry" +msgstr "ادخال بيانات" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__data_source +msgid "Data source" +msgstr "مصدر البيانات" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__date_field_name +msgid "Date Field Name" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__date_field_ids +msgid "Date Fields" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__period_passed +#, python-format +msgid "Days Passed" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#, python-format +msgid "" +"Days of periods which are already in the Past. If period is not yet started " +"- 1. If the whole period is in the Past - period length in days" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.res_config_settings_view_form +msgid "" +"Define how to distinguish similar periods. For example, 7-days periods might be\n" +" compared to 5-days and 9-days periods. Then, tolerance would be 2. If you have\n" +" monthly or quartely periods you should have at least 3-days tolerance" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_kanban +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +msgid "Delete Target" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "Details" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__data_source__direct_data +msgid "Direct Data" +msgstr "بيانات مباشرة" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__display_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__display_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_help__display_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__display_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__display_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__display_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__display_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_company__display_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_config_settings__display_name +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_category__display_name +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__display_name +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__display_name +msgid "Display Name" +msgstr "الاسم المعروض" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/js/scorecard_kanbancontroller.js:0 +#, python-format +msgid "" +"Do you really want to close the period? Actual values by its targets would " +"be frozen then." +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/js/scorecard_kanbancontroller.js:0 +#, python-format +msgid "Do you really want to delete this target?" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__kpi_help_dummy +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__kpi_help_dummy +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__kpi_help_dummy +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_help__kpi_help_dummy +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__kpi_help_dummy +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__kpi_help_dummy +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__kpi_help_dummy +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__kpi_help_dummy +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__kpi_help_dummy +msgid "Dummy Help" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +msgid "Edit Rights" +msgstr "صلاحية التعديل" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__edit_access_user_ids +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__edit_access_user_ids +msgid "Edit Rights Access Users" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__edit_user_ids +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__edit_user_ids +msgid "Edit Rights Allowed Users" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__edit_user_group_ids +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__edit_user_group_ids +msgid "Edit Rights User Groups" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type__effectiveness +msgid "Effectiveness" +msgstr "فاعلية" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type__efficiency +msgid "Efficiency" +msgstr "كفاءة" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_scorecard_line__result__error +msgid "Error" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_measure__measure_type__py_code +msgid "Execute Python code" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#, python-format +msgid "Export Scorecard" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__external_bench_ids +msgid "External Benchmarking" +msgstr "جهة المقارنة المرجعية الخارجية" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__domain +msgid "Extra Filters" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +msgid "Extra Targets Security" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_search +msgid "Failed Targets" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_scorecard_line__result__failure +msgid "Failure" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__domain +msgid "Filters" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type__financial +msgid "Financial" +msgstr "مالي" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__formula +msgid "Formula" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__target_value +msgid "Global Value" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_constant_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_item_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_search +msgid "Group By" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__help_notes +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__help_notes +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__help_notes +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_help__help_notes +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__help_notes +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__help_notes +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__help_notes +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__help_notes +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__help_notes +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_constant_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +msgid "Help" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_kanban +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +msgid "History" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_company__kpi_history_tolerance +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_config_settings__kpi_history_tolerance +msgid "History Tolerance" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#, python-format +msgid "History by similar periods" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_help__id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__id +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_company__id +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_config_settings__id +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_category__id +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__id +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__id +msgid "ID" +msgstr "المُعرف" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_measure.py:0 +#, python-format +msgid "In order to activate the KPI, please install the module for {}" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type_region__inputs +msgid "Inputs" +msgstr "مدخلات" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__existing_kpi +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__existing_kpi +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_search +msgid "Installed" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_domain__institute +msgid "Institutional Accreditation" +msgstr "الاعتماد المؤسسي" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__internal_bench_ids +msgid "Internal Benchmarking" +msgstr "جهة المقارنة المرجعية الداخلية" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_category.py:0 +#, python-format +msgid "It is not allowed to make recursions!" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/res_config_settings.py:0 +#, python-format +msgid "Job: Calculate KPIs" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: code:addons/kpi_scorecard/models/kpi_period.py:0 +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model:ir.actions.act_window,name:kpi_scorecard.kpi_item_action +#: model:ir.model,name:kpi_scorecard_odex.model_kpi_item +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__kpi_id +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_main +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +#, python-format +msgid "KPI" +msgstr "المؤشرات" + +#. module: kpi_scorecard +#: model:ir.actions.act_window,name:kpi_scorecard.kpi_category_action +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_categories +msgid "KPI Categories" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model,name:kpi_scorecard_odex.model_kpi_category +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_form +msgid "KPI Category" +msgstr "الهدف التشغيلي" + +#. module: kpi_scorecard_odex +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__code +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__code +#, python-format +msgid "KPI Code" +msgstr "كود المؤشر" + +#. module: kpi_scorecard +#: model:ir.model,name:kpi_scorecard.model_kpi_constant +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__constant_id +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_constant_view_form +msgid "KPI Constant" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__kpi_domain +msgid "KPI Domain" +msgstr "مجال المؤشر" + +#. module: kpi_scorecard +#: model:ir.model,name:kpi_scorecard.model_kpi_help +msgid "KPI Help" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/js/scorecard_kanbancontroller.js:0 +#, python-format +msgid "KPI History" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.module.category,name:kpi_scorecard.module_category_kpi +msgid "KPI Management" +msgstr "" + +#. module: kpi_scorecard +#: model:res.groups,name:kpi_scorecard.group_kpi_admin +msgid "KPI Manager" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_form +msgid "KPI Measure" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model,name:kpi_scorecard.model_kpi_measure +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_item_view_form +msgid "KPI Measurement" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model,name:kpi_scorecard.model_kpi_measure_item +msgid "KPI Measurement (Variable)" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__kpi_owner_id +msgid "KPI Owner" +msgstr "مالك الموشر" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model,name:kpi_scorecard.model_kpi_period +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__kpi_period +msgid "KPI Period" +msgstr "مدة المؤشر" + +#. module: kpi_scorecard +#: model:ir.model,name:kpi_scorecard.model_kpi_period_value +msgid "KPI Period Value" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/js/scorecard_kanbanview.js:0 +#: model:ir.actions.act_window,name:kpi_scorecard.kpi_scorecard_line_action +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.res_config_settings_view_form +#, python-format +msgid "KPI Scorecard" +msgstr "تقرير مؤشرات اﻷداء" + +#. module: kpi_scorecard_odex +#: model:ir.model,name:kpi_scorecard_odex.model_kpi_scorecard_line +msgid "KPI Target" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__line_ids +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__line_ids +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +msgid "KPI Targets" +msgstr "المستهدف" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__measure_type +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__measure_type +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__kpi_type +msgid "KPI Type" +msgstr "نمط الموشر" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__kpi_type_region +msgid "KPI Type By Region" +msgstr "نوع المؤشر حسب المنطقة" + +#. module: kpi_scorecard +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_kpi +msgid "KPIs" +msgstr "المؤشرات" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_form +msgid "KPIs Targets Security" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__last_recalculation_date +msgid "Last KPI Calculation" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant____last_update +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template____last_update +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_help____last_update +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure____last_update +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item____last_update +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period____last_update +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value____last_update +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_company____last_update +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_config_settings____last_update +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_category____last_update +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item____last_update +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line____last_update +msgid "Last Modified on" +msgstr "آخر تعديل في" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__write_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__write_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__write_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__write_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__write_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__write_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__write_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__write_uid +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__write_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__write_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__write_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__write_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__write_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__write_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__write_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__write_date +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__write_date +msgid "Last Updated on" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__computation_error +msgid "Logs" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,help:kpi_scorecard.field_kpi_measure__py_code +msgid "" +"Make sure the 'result' is defined and it contains the new current value." +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__measure_field_id +msgid "Measure Field" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__measure_field_name +msgid "Measure Field Name" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__measure_id +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_item_view_search +msgid "Measurement" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__measurement_method +msgid "Measurement Method" +msgstr "طريقة القياس " + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__measurement_periodic +msgid "Measurement Periodicity" +msgstr "دورية القياس" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_search +msgid "Measurement Type" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__measures_ids +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__item_ids +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_measurements_general +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +#, python-format +msgid "Measurements" +msgstr "المقاييس" + +#. module: kpi_scorecard +#: model:ir.actions.act_window,name:kpi_scorecard.kpi_measure_item_action +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_measures +msgid "Measurements (Variables)" +msgstr "ممقاييس المؤشرات" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__measures_len +msgid "Measurements Count" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +msgid "Misc" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__model_id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__model_id +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_item_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_search +msgid "Model" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__model_name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__model_name +msgid "Model Name" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_item__result_appearance__monetory +msgid "Monetary" +msgstr "نقدي" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: code:addons/kpi_scorecard/models/kpi_scorecard_line.py:0 +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#, python-format +msgid "N/A" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__name +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__name +msgid "Name" +msgstr "الاسم" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#, python-format +msgid "New Period" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: code:addons/kpi_scorecard/models/kpi_period.py:0 +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__description +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__description +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__description +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__description +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__description +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_constant_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_form +#, python-format +msgid "Notes" +msgstr "ملاحظات" + +#. modules: kpi_scorecard,kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__description +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +msgid "Notes" +msgstr "وصف المؤشر" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_item__result_appearance__number +msgid "Number" +msgstr "عدد" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_search +msgid "Open" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/js/scorecard_kanbancontroller.js:0 +#, python-format +msgid "Open New Period" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_period__state__open +msgid "Opened" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_category__type__op_goal +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_search +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_tree +msgid "Operational Goal" +msgstr "هدف تشغيلي" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#, python-format +msgid "Operators" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__data_source__poll +msgid "Opinion Polls" +msgstr "استطلاعات رأي" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_category__type__other +msgid "Other" +msgstr "اخرى" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__kpi_ids +#, python-format +msgid "Other KPIs" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-f.ormat +msgid "" +"Other KPIs used in formula depends on this KPI. Calculation is impossible" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type_region__outputs +msgid "Outputs" +msgstr "مخرجات" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__parent_id +msgid "Parent Category" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__parent_id +msgid "Parent KPI" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__parent_id +msgid "Parent Period" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_item__result_appearance__percentage +msgid "Percentage" +msgstr "نسبة" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__period_id +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__period_id +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +#, python-format +msgid "Period" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__period_length +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__period_length +#, python-format +msgid "Period Days" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__date_end +msgid "Period End" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__date_start +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_search +msgid "Period Start" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_period.py:0 +#: model:ir.model.constraint,message:kpi_scorecard.constraint_kpi_period_dates_check +#, python-format +msgid "Period end should be after period start" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#, python-format +msgid "Period length in days" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_period_value.py:0 +#: model:ir.model.constraint,message:kpi_scorecard.constraint_kpi_period_value_period_constant_id_uniq +#, python-format +msgid "Period should be unique per each constant!" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__measurement_periodic__periodical +msgid "Periodical" +msgstr "فصلي" + +#. module: kpi_scorecard +#: model:ir.actions.act_window,name:kpi_scorecard.kpi_period_action +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_period +msgid "Periods" +msgstr "الفترات" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_category__type__pillar +msgid "Pillar" +msgstr "ركيزة" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type_region__processes +msgid "Processes" +msgstr "عمليات" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type__productivity +msgid "Productivity" +msgstr "إنتاجية" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__program_id +msgid "Program" +msgstr "البرنامج" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_domain__program +msgid "Program Accreditation" +msgstr "اعتماد برامجي" + +#. module: kpi_scorecard_odex +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__progress +#, python-format +msgid "Progress" +msgstr "الانجاز" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__py_code +msgid "Python Code" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type__quality +msgid "Quality" +msgstr "جودة" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +#, python-format +msgid "Re-Open Period" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_search +msgid "Reached Targets" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +msgid "Read Rights" +msgstr "صلاحية اطلاع" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__access_user_ids +msgid "Read Rights Access Users" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__user_group_ids +msgid "Read Rights User Groups" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__user_ids +msgid "Read Rights Users" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__name +msgid "Reference" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__result +msgid "Result" +msgstr "النتيجة" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__result_preffix +msgid "Result Prefix" +msgstr "بادئة" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__result_suffix +msgid "Result Suffix" +msgstr "لاحقة" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__result_appearance +msgid "Result Type" +msgstr "نوع النتيجة" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__result_rounding +msgid "Rounding Decimals" +msgstr "التقريب" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_domain__research +msgid "Scientific Research" +msgstr "البحث العلمي" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#: code:addons/kpi_scorecard/static/src/xml/kpi_formula.xml:0 +#, python-format +msgid "Search" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_type__security +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +msgid "Security" +msgstr "الصلاحيات" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.res_config_settings_view_form +msgid "Select company to configure KPI management settings" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_category__sequence +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__sequence +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__sequence +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure__sequence +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_measure_item__sequence +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__sequence +msgid "Sequence" +msgstr "متسلسل" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/js/scorecard_kanbancontroller.js:0 +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#, python-format +msgid "Set Target" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_constant__periods_ids +msgid "Set for periods" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.actions.act_window,name:kpi_scorecard.res_config_settings_kpi_scorecard_action +#: model:ir.ui.menu,name:kpi_scorecard.menu_kpi_settings +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +msgid "Settings" +msgstr "الاعدادات" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_company__show_kpi_help +#: model:ir.model.fields,field_description:kpi_scorecard.field_res_config_settings__show_kpi_help +msgid "Show Help Tabs" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period__state +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_search +msgid "State" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_category__type__st_goal +msgid "Strategic Goal" +msgstr "هدف استراتيجي" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_copy_template_wizard_form_view +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +#, python-format +msgid "Substitute Targets" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__template_id +msgid "Substitute Targets With" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.actions.act_window,name:kpi_scorecard.kpi_copy_template_action +msgid "Substitute Targets with Targets from Another Period" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_scorecard_line__result__success +#, python-format +msgid "Success" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_item__result_type +msgid "Success Criteria" +msgstr "معايير النجاح" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__target_value +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +msgid "Success Value" +msgstr "نجاح المؤشر" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_measure__measure_type__sum +msgid "Sum of records field" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#. openerp-web +#: code:addons/kpi_scorecard/models/kpi_period.py:0 +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_scorecard_line__formatted_target_value +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__formatted_planed_value +#, python-format +msgid "Target" +msgstr "المستهدف" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_period_value__target_value +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__planed_value +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_scorecard_line_view_kanban +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +msgid "Target Value" +msgstr "المستهدف" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_scorecard_line.py:0 +#: model:ir.model.constraint,message:kpi_scorecard.constraint_kpi_scorecard_line_period_kpi_uniq +#, python-format +msgid "Target for this KPI is already set for this period" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +msgid "Targets" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#, python-format +msgid "Targets by all periods" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_category_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_constant_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_item_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_measure_view_form +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +msgid "Technical" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model,name:kpi_scorecard.model_kpi_copy_template +msgid "Template Targets Replace" +msgstr "" + +#. modules: kpi_scorecard, kpi_scorecard_odex +#: code:addons/kpi_scorecard/models/kpi_period.py:0 +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#, python-format +msgid "" +"The Python library xlsxwriter is installed. Contact your system " +"administrator" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "The formula is empty" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "The formula might be incorrect: {}" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "The formula might rely upon incorrect KPI: {}.>> {}" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "The formula uses obsolete measurement: {}" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_item__result_type__less +msgid "The less the better" +msgstr "اﻷقل اﻷفضل" + +#. module: kpi_scorecard +#: model:ir.model.fields.selection,name:kpi_scorecard.selection__kpi_item__result_type__more +msgid "The more the better" +msgstr "اﻷكثر اﻷفضل" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "There are KPIs which depend on this KPI: {}. Delete them before" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_measure_item.py:0 +#, python-format +msgid "" +"There are KPIs which depend on this MEASUREMENT: {}. Delete them before" +msgstr "" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_constant.py:0 +#, python-format +msgid "There are KPIs which depend on this constant: {}. Delete them before" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,field_description:kpi_scorecard.field_kpi_copy_template__period_id +msgid "This Period" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.res_config_settings_view_form +msgid "Turn on / off help notes on KPI settings objects" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_category__type +msgid "Type" +msgstr "نوع" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__kpi_domain__university +msgid "University Strategy" +msgstr "استراتيجية الجامعة" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/js/scorecard_kanbancontroller.js:0 +#, python-format +msgid "Update Target" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,help:kpi_scorecard.field_kpi_constant__target_value +msgid "Value used in case this constant is not applied for a target period" +msgstr "" + +#. module: kpi_scorecard_odex +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_category__weight +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_item__weight +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__weight +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_tree +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +#, python-format +msgid "Weight" +msgstr "الوزن" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields,field_description:kpi_scorecard_odex.field_kpi_scorecard_line__weight_progress +msgid "Weight Progress" +msgstr "الوزن المحقق" + +#. module: kpi_scorecard_odex +#: code:addons/kpi_scorecard_odex/models/kpi_scorecard_line.py:0 +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_scorecard_line_view_kanban +#, python-format +msgid "Weight progress" +msgstr "الوزن المحقق" + +#. module: kpi_scorecard +#: code:addons/kpi_scorecard/models/kpi_item.py:0 +#, python-format +msgid "You cannot create recursive KPIs." +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.customer_invoice_all +msgid "[account] Posted Customer Invoices: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.customer_invoice_amount_all +#: model:kpi.measure,name:kpi_scorecard.vendor_bills_amount_all +msgid "[account] Posted Customer Invoices: Total" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.vendor_invoice_all +msgid "[account] Posted Vendor Bills: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_all +msgid "[crm] Leads and Opportunities: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_leads_only +msgid "[crm] Leads: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_opportunities_days_to_assign +msgid "[crm] Opportunities: Average Days to Assign" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_opportunities_days_to_close +msgid "[crm] Opportunities: Average Days to Close" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_opportunities_expected_revenue +msgid "[crm] Opportunities: Average Expected Revenue" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_opportunities_sale_average +msgid "[crm] Opportunities: Average of Sale Orders" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_opportunities_only +msgid "[crm] Opportunities: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_opportunities_expected_revenue_total +msgid "[crm] Opportunities: Total Expected Revenue" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_opportunities_sale_total +msgid "[crm] Opportunities: Total Sale Orders" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.crm_lead_won_opprotunities +msgid "[crm] Won Opportunities: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.project_task_planned_hours_average +msgid "[project] Tasks: Average Planned Hours" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.project_task_all +msgid "[project] Tasks: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.project_task_planned_hours +msgid "[project] Tasks: Total Planned Hours" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.project_task_working_days_to_assign +msgid "[project] Tasks: Working Days to Assign" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.project_task_working_days_to_close +msgid "[project] Tasks: Working Days to Close" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.sale_order_all_abs +msgid "[sales] All Sale Orders and Quotations: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.sale_order_all +msgid "[sales] Сonfirmed/Done Sale Orders: Count" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.sale_order_delivered_qty +msgid "[sales] Сonfirmed/Done Sale Orders: Delivered Units" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.sale_order_invoiced_qty +msgid "[sales] Сonfirmed/Done Sale Orders: Invoiced Units" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.sale_order_number_of_lines +msgid "[sales] Сonfirmed/Done Sale Orders: Number of Lines" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.sale_order_total +msgid "[sales] Сonfirmed/Done Sale Orders: Total" +msgstr "" + +#. module: kpi_scorecard +#: model:kpi.measure,name:kpi_scorecard.sale_order_total_qty +msgid "[sales]Сonfirmed/Done Sale Orders: Product Units" +msgstr "" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +msgid "e.g. Year 2021" +msgstr "" + +#. module: kpi_scorecard +#. openerp-web +#: code:addons/kpi_scorecard/static/src/xml/kpi_kanban.xml:0 +#, python-format +msgid "if closed, KPI actual values would not be re-calculated any more" +msgstr "" + +#. module: kpi_scorecard_odex +#: model:ir.model.fields.selection,name:kpi_scorecard_odex.selection__kpi_item__measurement_method__integration +msgid "integration" +msgstr "تكامل" + +#. module: kpi_scorecard_odex +#: model_terms:ir.ui.view,arch_db:kpi_scorecard_odex.kpi_item_view_form +msgid "kpiFormula" +msgstr "المعادلة" + +#. module: kpi_scorecard +#: model_terms:ir.ui.view,arch_db:kpi_scorecard.kpi_period_view_form +msgid "to" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,help:kpi_scorecard.field_kpi_item__result_suffix +msgid "would be shown after the result value" +msgstr "" + +#. module: kpi_scorecard +#: model:ir.model.fields,help:kpi_scorecard.field_kpi_item__result_preffix +msgid "would be shown before the result value" +msgstr "" diff --git a/odex25_project/kpi_scorecard_odex/models/__init__.py b/odex25_project/kpi_scorecard_odex/models/__init__.py new file mode 100644 index 000000000..be398e78e --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/models/__init__.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + + +from . import kpi_category +from . import kpi_item +from . import kpi_scorecard_line +from . import kpi_period +from . import partner_target + + + + + diff --git a/odex25_project/kpi_scorecard_odex/models/kpi_category.py b/odex25_project/kpi_scorecard_odex/models/kpi_category.py new file mode 100644 index 000000000..050b3c913 --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/models/kpi_category.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# (Odex - Extending the base module). +# Copyright (C) 2024 Expert Co. Ltd. (). +# +############################################################################## + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class kpi_category(models.Model): + + _inherit = "kpi.category" + + code = fields.Char(string="Code") + weight = fields.Float(string="Weight") + type = fields.Selection([ + ("pillar", "Pillar"), + ("st_goal", "Strategic Goal"), + ("op_goal", "Operational Goal"), + ("other", "Other")],string="Type", default="op_goal") + + def name_get(self): + return super(models.Model, self).name_get() + + + def write(self, data): + res = super(kpi_category, self).write(data) + if data.get('weight'): + self.env["kpi.item"].compute_automated_weight(self) + return res + + diff --git a/odex25_project/kpi_scorecard_odex/models/kpi_item.py b/odex25_project/kpi_scorecard_odex/models/kpi_item.py new file mode 100644 index 000000000..09cb11863 --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/models/kpi_item.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# (Odex - Extending the base module). +# Copyright (C) 2024 Expert Co. Ltd. (). +# +############################################################################## + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class kpi_item(models.Model): + + _inherit = "kpi.item" + + code = fields.Char(string="KPI Code", required=True) + weight = fields.Float(string="Weight") + automated_weight = fields.Boolean(string="Automated Weight", default=True) + kpi_domain = fields.Selection([ + ("university", "University Strategy"), + ("research", "Scientific Research"), + ("institute", "Institutional Accreditation"), + ("program", "Program Accreditation")], string="KPI Domain", required=True, default="university") + program_id = fields.Many2one("kpi.category", string="Program") + kpi_type = fields.Selection([ + ("efficiency", "Efficiency"), + ("effectiveness", "Effectiveness"), + ("productivity", "Productivity"), + ("financial", "Financial"), + ("quality", "Quality"), + ("security", "Security")], string="KPI Type", required=True, default="efficiency") + kpi_type_region = fields.Selection([ + ("inputs", "Inputs"), + ("processes", "Processes"), + ("outputs", "Outputs"),], string="KPI Type By Region", required=True, default="inputs") + data_source = fields.Selection([ + ("achievement", "Achievement"), + ("direct_data", "Direct Data"), + ("poll", "Opinion Polls"),], string="Data source", required=True, default="achievement") + measurement_method = fields.Selection([ + ("data_entry", "Data entry"), + ("integration", "integration"),], string="Measurement Method", required=True, default="data_entry") + measurement_periodic= fields.Selection([ + ("periodical", "Periodical"), + ("annually", "Annually"),], string="Measurement Periodicity", required=True, default="annually") + kpi_owner_id = fields.Many2one("res.partner", string="KPI Owner") + internal_bench_ids = fields.Many2many("res.partner", + "res_partner_kpi_item_rel_internal", + "res_partner_id", "kpi_item_id", string="Internal Benchmarking") + external_bench_ids = fields.Many2many("res.partner", + "res_partner_kpi_item_rel_external", + "res_partner_id", "kpi_item_id", string="External Benchmarking") + + kpi_period = fields.Selection([ + ("1", "1"), + ("2", "2"), + ("3", "3"), + ("4", "4"),("5", "5") ], string="KPI Period", default="5") + + line_ids = fields.One2many("kpi.scorecard.line", "kpi_id", string="KPI Targets", copy=True) + + + def compute_automated_weight(self, category_ids): + for category in category_ids: + goal_kpis = self.env["kpi.item"].sudo().search([ ("category_id", "=", category.id),("automated_weight", "=", True)]) + goal_kpis_edit = self.env["kpi.item"].sudo().search([ ("category_id", "=", category.id),("automated_weight", "=", False)]) + goal_weight = category.weight - sum(goal_kpis_edit.mapped('weight')) + if len(goal_kpis) > 0: + weight = round(goal_weight / len(goal_kpis) , 2) + goal_kpis.write({"weight": weight}) + + @api.model + def create(self, data): + res = super(kpi_item, self).create(data) + category_ids =[] + for kpi in res: + category_ids.append(kpi.category_id) + self.compute_automated_weight(category_ids) + return res + + def write(self, data): + category_ids =[] + for kpi in self: + if data.get('category_id'): + category_ids.append(kpi.category_id) + res = super(kpi_item, self).write(data) + if data.keys() & {'automated_weight', 'active', 'category_id'}: + for kpi in self: + category_ids.append(kpi.category_id) + if data.get('weight') and not data.get('automated_weight'): + for kpi in self: + if not kpi.automated_weight: + category_ids.append(kpi.category_id) + self.compute_automated_weight(category_ids) + return res + + def unlink(self): + category_ids =[] + for kpi in self: + category_ids.append(kpi.category_id) + res = super(kpi_item, self).unlink() + self.compute_automated_weight(category_ids) + return res + + @api.onchange('category_id') + def onchange_category_id(self): + ''' + This function generates the kpi code automatically with the possibility to modify it + ''' + if self.category_id and self.category_id.code: + parent_code = self.category_id.code + count = len (self.search([("category_id", "=", self.category_id.id)])) or 1 + serial = int(count) + added = 0 + code = '' + while True: + serial += added + code_length = len(str(parent_code)) + len(str(serial)) + code = parent_code +str(serial) + if not self.search([('code','=',code)]): + break + added += 1 + self.code = code + + + + diff --git a/odex25_project/kpi_scorecard_odex/models/kpi_period.py b/odex25_project/kpi_scorecard_odex/models/kpi_period.py new file mode 100644 index 000000000..056d05763 --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/models/kpi_period.py @@ -0,0 +1,111 @@ +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError, UserError +import base64 +import logging +import tempfile + +_logger = logging.getLogger(__name__) + +try: + import xlsxwriter +except ImportError: + _logger.warning("Cannot import xlsxwriter") + xlsxwriter = False + + +class kpi_period(models.Model): + _inherit = "kpi.period" + + def name_get(self): + return super(models.Model, self).name_get() + + def action_export_scorecard(self): + """ + The method to prepare the xls table + + Methods: + * _get_xls_table of kpi.scorecard.line + + Returns: + * action of downloading the xlsx table + + Extra info: + * Expected singleton + """ + self.ensure_one() + if not xlsxwriter: + raise UserError(_("The Python library xlsxwriter is installed. Contact your system administrator")) + file_name = u"{}.xlsx".format(self.name_get()[0][1]) + file_path = tempfile.mktemp(suffix='.xlsx') + workbook = xlsxwriter.Workbook(file_path) + main_header_style = workbook.add_format({ + 'bold': True, + 'font_size': 11, + 'border': 1, + 'align': 'center', + 'valign': 'vcenter', + 'bg_color': 'silver', + 'border_color': 'gray', + }) + main_cell_style_dict = { + 'font_size': 11, + 'border': 1, + 'border_color': 'gray', + } + worksheet = workbook.add_worksheet(file_name) + column_keys = [ + {"key": "A", "label": _("KPI Code"), "width": 14}, + {"key": "B", "label": _("KPI"), "width": 60}, + {"key": "C", "label": _("Target"), "width": 14}, + {"key": "D", "label": _("Success"), "width": 14}, + {"key": "E", "label": _("Actual"), "width": 14}, + {"key": "F", "label": _("Progress"), "width": 14}, + {"key": "G", "label": _("Weight"), "width": 14}, + {"key": "H", "label": _("Weight progress"), "width": 14}, + {"key": "I", "label": _("Notes"), "width": 80}, + ] + total_row_number = len(self.line_ids) + cell_values = self.line_ids._get_xls_table() + for ccolumn in column_keys: + ckey = ccolumn.get("key") + # set columns + worksheet.set_column('{c}:{c}'.format(c=ckey), ccolumn.get("width")) + # set header row + worksheet.write("{}1".format(ckey), ccolumn.get("label"), main_header_style) + # set column values + for row_number in range(2, total_row_number + 2): + cell_number = "{}{}".format(ckey, row_number) + cell_value_dict = cell_values.get(cell_number) + cell_value = "" + cell_level = 0 + cell_style = main_cell_style_dict.copy() + if cell_value_dict: + cell_value = cell_value_dict.get("value") + cell_style.update(cell_value_dict.get("style")) + cell_level = cell_value_dict.get("level") or 0 + cell_style = workbook.add_format(cell_style) + if ckey == "A": + cell_style.set_indent(cell_level) + worksheet.write( + cell_number, + cell_value, + cell_style, + ) + worksheet.set_row(0, 24) + workbook.close() + with open(file_path, 'rb') as r: + xls_file = base64.b64encode(r.read()) + att_vals = { + 'name': file_name, + 'type': 'binary', + 'datas': xls_file, + } + attachment_id = self.env['ir.attachment'].create(att_vals) + self.env.cr.commit() + action = { + 'type': 'ir.actions.act_url', + 'url': '/web/content/{}?download=true'.format(attachment_id.id, ), + 'target': 'self', + } + return action + diff --git a/odex25_project/kpi_scorecard_odex/models/kpi_scorecard_line.py b/odex25_project/kpi_scorecard_odex/models/kpi_scorecard_line.py new file mode 100644 index 000000000..0b527965a --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/models/kpi_scorecard_line.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# (Odex - Extending the base module). +# Copyright (C) 2024 Expert Co. Ltd. (). +# +############################################################################## +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError, UserError +import base64 +import logging +import tempfile + +_logger = logging.getLogger(__name__) + +try: + import xlsxwriter +except ImportError: + _logger.warning("Cannot import xlsxwriter") + xlsxwriter = False + + +class kpi_scorecard_line(models.Model): + _inherit = "kpi.scorecard.line" + + @api.depends("target_value", "actual_value", "computation_error", "kpi_id", "kpi_id.result_appearance", + "kpi_id.result_preffix", "kpi_id.result_suffix", "kpi_id.result_type") + def _compute_formatted_actual_value(self): + """ + Compute method for formatted_actual_value, result, formatted_target_value + + Methods: + * _return_formated_appearance of kpi.item + """ + for line in self: + kpi_id = line.kpi_id + company_currency = line.period_id.company_id.currency_id + if line.computation_error: + line.formatted_actual_value = _("N/A") + line.result = "error" + line.formatted_target_value = kpi_id._return_formated_appearance( + line.target_value, novalue_change=True, currency=company_currency, + ) + line.formatted_planed_value = kpi_id._return_formated_appearance( + line.planed_value, novalue_change=True, currency=company_currency, + ) + else: + actual_value = line.actual_value + line.formatted_actual_value = kpi_id._return_formated_appearance( + line.actual_value, novalue_change=False, currency=company_currency, + ) + line.formatted_planed_value = kpi_id._return_formated_appearance( + line.planed_value, novalue_change=True, currency=company_currency, + ) + line.formatted_target_value = kpi_id._return_formated_appearance( + line.target_value, novalue_change=True, currency=company_currency, + ) + result_type = kpi_id.result_type + actual_value = line.kpi_id.result_appearance == "percentage" and (actual_value * 100) or actual_value + bigger_result = actual_value >= line.target_value + if result_type == "more": + line.result = bigger_result and "success" or "failure" + elif result_type == "less": + line.result = bigger_result and "failure" or "success" + + weight = fields.Float(string="Weight", related="kpi_id.weight", store=True) + code = fields.Char(string="KPI Code", related="kpi_id.code", store=True) + progress = fields.Float("Progress", compute='_compute_progress_kpi', store=True, group_operator="avg") + weight_progress = fields.Float("Weight Progress", compute='_compute_progress_kpi', store=True, ) + target_value = fields.Float(string="Success Value") + planed_value = fields.Float(string="Target Value") + formatted_planed_value = fields.Char( + string="Target", + compute=_compute_formatted_actual_value, + compute_sudo=True, + store=True, + ) + + pillar_categ_id = fields.Many2one(comodel_name='kpi.category', compute='_compute_parent_category', store=True, ) + strategic_category_id = fields.Many2one(comodel_name='kpi.category', compute='_compute_parent_category', + store=True, ) + plan_progress = fields.Float(string="Planned Progress", compute='_compute_progress_kpi', store=True, + ) + + def get_strategic_category(self, category_id): + """ + Get the last parent category with type 'st_goal' in the chain of categories. + + :param category_id: The initial category for which to find the last 'pillar' parent. + :return: The last Strategic Goal parent category or None if not found. + """ + if category_id.type == 'st_goal': + return category_id + + while category_id.parent_id: + category_id = category_id.parent_id + if category_id.type == 'st_goal': + return category_id + return None + + def get_pillar_category(self, category_id): + """ + Get the last parent category with type 'pillar' in the chain of categories. + + :param category_id: The initial category for which to find the last 'pillar' parent. + :return: The last 'pillar' parent category or None if not found. + """ + if category_id.type == 'pillar': + return category_id + + while category_id.parent_id: + category_id = category_id.parent_id + if category_id.type == 'pillar': + return category_id + return None + + def action_compare_with_partners_targets(self): + return self.env['ir.actions.act_window']._for_xml_id('kpi_scorecard_odex.scorecard_line_tree_action') + + @api.depends('kpi_id', 'kpi_id.category_id') + def _compute_parent_category(self): + for line in self: + last_pillar_categ = line.get_pillar_category(line.category_id) + last_strategic_categ = line.get_strategic_category(line.category_id) + line.pillar_categ_id = last_pillar_categ.id if last_strategic_categ else False + line.strategic_category_id = last_strategic_categ.id if last_strategic_categ else False + + @api.depends('target_value', 'actual_value', 'weight') + def _compute_progress_kpi(self): + for kpi in self: + if (kpi.target_value > 0.0): + if kpi.actual_value > kpi.target_value: + kpi.progress = 100 + else: + kpi.progress = round(100.0 * kpi.actual_value / kpi.target_value, 2) + else: + kpi.progress = 0.0 + + kpi.weight_progress = round((kpi.progress * kpi.weight) / 100.0, 2) + kpi.plan_progress = kpi.progress * kpi.weight + + def _get_xls_table(self): + """ + The method to prepare dict of values for xls row + + Args: + * spaces - str - to add at the beginning of the name + + Methods: + * _return_xls_formatting - of kpi.item + + Returns: + * dict + """ + result = {} + row = 2 + previous_kpis = {} + for line in self: + parent_id = line.kpi_id.parent_id.id + level = previous_kpis.get(parent_id) is not None and previous_kpis.get(parent_id) + 1 or 0 + previous_kpis.update({line.kpi_id.id: level}) + description = line.description or "" + target_value = line.target_value + planed_value = line.planed_value + actual_value = line.actual_value + overall_style = { + "color": line.result == "success" and "black" or line.result == "failure" and "red" or "orange" + } + if line.computation_error: + target_value = 0 + planed_value = 0 + actual_value = 0 + description = "{} {}".format(line.computation_error, description) + overall_style.update({"color": "orange"}) + planed_value = line.kpi_id._return_xls_formatting(line.planed_value, False) + target_value = line.kpi_id._return_xls_formatting(line.target_value, False) + actual_value = line.kpi_id._return_xls_formatting(line.actual_value, True) + num_style = overall_style.copy() + num_style.update({ + "align": "center", + }) + num_style1 = num_style.copy() + + if line.kpi_id.result_appearance == "percentage": + num_style.update({"num_format": 10}), + overall_style.update({ + "valign": "vjustify", + }) + + result.update({ + "A{}".format(row): {"value": line.kpi_id.code, "style": overall_style, "level": level}, + "B{}".format(row): {"value": line.kpi_id.name, "style": overall_style, "level": level}, + "C{}".format(row): {"value": planed_value, "style": num_style}, + "D{}".format(row): {"value": target_value, "style": num_style}, + "E{}".format(row): {"value": actual_value, "style": num_style}, + "F{}".format(row): {"value": line.progress, "style": num_style1}, + "G{}".format(row): {"value": line.kpi_id.weight, "style": num_style1}, + "H{}".format(row): {"value": line.weight_progress, "style": num_style1}, + "I{}".format(row): {"value": description, "style": overall_style}, + }) + row += 1 + return result + + def action_assign_target_values(self): + self.ensure_one() + partner_target_ids = self.env['partner.target'].search([('kbi_line_id', '=', self.id)]) + action = { + 'name': _('Targets'), + 'type': 'ir.actions.act_window', + 'res_model': 'partner.target', + 'view_mode': 'tree', + 'context': {'default_kbi_line_id': self.id}, + 'target': 'current', + 'domain': [('id', 'in', partner_target_ids.ids)], + } + return action diff --git a/odex25_project/kpi_scorecard_odex/models/partner_target.py b/odex25_project/kpi_scorecard_odex/models/partner_target.py new file mode 100644 index 000000000..76128fc2a --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/models/partner_target.py @@ -0,0 +1,11 @@ +from odoo import fields, models, api + + +class PartnerTarget(models.Model): + _name = 'partner.target' + kbi_line_id = fields.Many2one(comodel_name='kpi.scorecard.line') + partner_id = fields.Many2one(comodel_name='res.partner') + planned_value = fields.Float() + target_value=fields.Float(related='kbi_line_id.target_value') + actual_value=fields.Float(related='kbi_line_id.actual_value') + diff --git a/odex25_project/kpi_scorecard_odex/reports/__init__.py b/odex25_project/kpi_scorecard_odex/reports/__init__.py new file mode 100644 index 000000000..7c68785e9 --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/reports/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- \ No newline at end of file diff --git a/odex25_project/kpi_scorecard_odex/security/ir.model.access.csv b/odex25_project/kpi_scorecard_odex/security/ir.model.access.csv new file mode 100644 index 000000000..402c9b60c --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_partner_target,access.partner.target,model_partner_target,,1,1,1,1 diff --git a/odex25_project/kpi_scorecard_odex/views/kpi_category.xml b/odex25_project/kpi_scorecard_odex/views/kpi_category.xml new file mode 100644 index 000000000..d40e1cd8c --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/views/kpi_category.xml @@ -0,0 +1,38 @@ + + + + + + kpi.category.form.inherit + kpi.category + + + + + + + + + + + + kpi.category.tree.inherit + kpi.category + + + + + + + + + + + + + + + + + + diff --git a/odex25_project/kpi_scorecard_odex/views/kpi_item.xml b/odex25_project/kpi_scorecard_odex/views/kpi_item.xml new file mode 100644 index 000000000..d9bc473f5 --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/views/kpi_item.xml @@ -0,0 +1,253 @@ + + + + + + kpi.item.form.inherit + kpi.item + + + + +
+ +
+

+ +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + kpi.scorecard.line.pivot + kpi.scorecard.line + + + + + + + + + + + + + + kanban,pivot + +
diff --git a/odex25_project/kpi_scorecard_odex/views/menu_security_cus.xml b/odex25_project/kpi_scorecard_odex/views/menu_security_cus.xml new file mode 100644 index 000000000..c6c781d94 --- /dev/null +++ b/odex25_project/kpi_scorecard_odex/views/menu_security_cus.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/odex25_project/odex25_helpdesk_sale/__init__.py b/odex25_project/odex25_helpdesk_sale/__init__.py new file mode 100644 index 000000000..cde864bae --- /dev/null +++ b/odex25_project/odex25_helpdesk_sale/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/odex25_project/odex25_helpdesk_sale/__manifest__.py b/odex25_project/odex25_helpdesk_sale/__manifest__.py new file mode 100644 index 000000000..bb2c4ba7b --- /dev/null +++ b/odex25_project/odex25_helpdesk_sale/__manifest__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +{ + 'name': 'Helpdesk After Sales', + 'category': 'Odex25-Project/Odex25-Project', + 'summary': 'Project, Tasks, After Sales', + 'author': "Expert Co. Ltd.", + 'website': "http://www.exp-sa.com", + 'depends': ['odex25_helpdesk', 'sale_management'], + 'auto_install': True, + 'description': """ +Manage the after sale of the products from helpdesk tickets. + """, + 'data': [ + 'views/odex25_helpdesk_views.xml', + ], + 'demo': ['data/odex25_helpdesk_sale_demo.xml'], +} diff --git a/odex25_project/odex25_helpdesk_sale/data/odex25_helpdesk_sale_demo.xml b/odex25_project/odex25_helpdesk_sale/data/odex25_helpdesk_sale_demo.xml new file mode 100644 index 000000000..4f496f622 --- /dev/null +++ b/odex25_project/odex25_helpdesk_sale/data/odex25_helpdesk_sale_demo.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + 1 + 3645.00 + + + + + + + + + + + + 1 + 14.00 + + + + + + + + + + + + 1 + 12.50 + + + + + + + + + + + + + + diff --git a/odex25_project/odex25_helpdesk_sale/i18n/ar.po b/odex25_project/odex25_helpdesk_sale/i18n/ar.po new file mode 100644 index 000000000..b93406335 --- /dev/null +++ b/odex25_project/odex25_helpdesk_sale/i18n/ar.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * odex25_helpdesk_sale +# +# Translators: +# Mustafa Rawi , 2020 +# Osama Ahmaro , 2020 +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server saas~13.5+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-09-01 07:39+0000\n" +"PO-Revision-Date: 2020-09-07 08:20+0000\n" +"Last-Translator: Osama Ahmaro , 2020\n" +"Language-Team: Arabic (https://www.transifex.com/odoo/teams/41243/ar/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: ar\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#. module: odex25_helpdesk_sale +#: model:ir.model.fields,field_description:odex25_helpdesk_sale.field_odex25_helpdesk_ticket__commercial_partner_id +msgid "Commercial Entity" +msgstr "الكيان التجاري" + +#. module: odex25_helpdesk_sale +#: model:ir.model.fields,field_description:odex25_helpdesk_sale.field_odex25_helpdesk_ticket__display_name +msgid "Display Name" +msgstr "الاسم المعروض" + +#. module: odex25_helpdesk_sale +#: model:ir.model,name:odex25_helpdesk_sale.model_odex25_helpdesk_ticket +msgid "Helpdesk Ticket" +msgstr "تذكرة مكتب المساعدة" + +#. module: odex25_helpdesk_sale +#: model:ir.model.fields,field_description:odex25_helpdesk_sale.field_odex25_helpdesk_ticket__id +msgid "ID" +msgstr "المُعرف" + +#. module: odex25_helpdesk_sale +#: model:ir.model.fields,field_description:odex25_helpdesk_sale.field_odex25_helpdesk_ticket____last_update +msgid "Last Modified on" +msgstr "آخر تعديل في" + +#. module: odex25_helpdesk_sale +#: model:ir.model.fields,field_description:odex25_helpdesk_sale.field_odex25_helpdesk_ticket__sale_order_id +msgid "Ref. Sales Order" +msgstr "" + +#. module: odex25_helpdesk_sale +#: model:ir.model.fields,help:odex25_helpdesk_sale.field_odex25_helpdesk_ticket__sale_order_id +msgid "" +"Reference of the Sales Order to which this ticket refers. Setting this " +"information aims at easing your After Sales process and only serves " +"indicative purposes." +msgstr "" diff --git a/odex25_project/odex25_helpdesk_sale/models/__init__.py b/odex25_project/odex25_helpdesk_sale/models/__init__.py new file mode 100644 index 000000000..27eb465e7 --- /dev/null +++ b/odex25_project/odex25_helpdesk_sale/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import odex25_helpdesk diff --git a/odex25_project/odex25_helpdesk_sale/models/odex25_helpdesk.py b/odex25_project/odex25_helpdesk_sale/models/odex25_helpdesk.py new file mode 100644 index 000000000..66e965b0b --- /dev/null +++ b/odex25_project/odex25_helpdesk_sale/models/odex25_helpdesk.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from odoo import api, fields, models + + +class odex25_helpdeskTicket(models.Model): + _inherit = 'odex25_helpdesk.ticket' + + commercial_partner_id = fields.Many2one(related='partner_id.commercial_partner_id') + sale_order_id = fields.Many2one('sale.order', string='Ref. Sales Order', + domain="""[ + '|', (not commercial_partner_id, '=', 1), ('partner_id', 'child_of', commercial_partner_id or []), + ('company_id', '=', company_id)]""", + groups="sales_team.group_sale_salesman,account.group_account_invoice", + help="Reference of the Sales Order to which this ticket refers. Setting this information aims at easing your After Sales process and only serves indicative purposes.") + + def copy(self, default=None): + if not self.env.user.has_group('sales_team.group_sale_salesman') and not self.env.user.has_group('account.group_account_invoice'): + if default is None: + default = {'sale_order_id': False} + else: + default.update({'sale_order_id': False}) + return super(odex25_helpdeskTicket, self).copy(default=default) diff --git a/odex25_project/odex25_helpdesk_sale/static/description/icon.png b/odex25_project/odex25_helpdesk_sale/static/description/icon.png new file mode 100644 index 000000000..4141f52da Binary files /dev/null and b/odex25_project/odex25_helpdesk_sale/static/description/icon.png differ diff --git a/odex25_project/odex25_helpdesk_sale/views/odex25_helpdesk_views.xml b/odex25_project/odex25_helpdesk_sale/views/odex25_helpdesk_views.xml new file mode 100644 index 000000000..492237564 --- /dev/null +++ b/odex25_project/odex25_helpdesk_sale/views/odex25_helpdesk_views.xml @@ -0,0 +1,44 @@ + + + + odex25_helpdesk.ticket.form.inherit.invoicing + odex25_helpdesk.ticket + + + + + + + + {'always_reload': True} + {'res_partner_search_mode': 'customer'} + + + + + + odex25_helpdesk.ticket.form.quick_create + odex25_helpdesk.ticket + + + + {'always_reload': True} + {'res_partner_search_mode': 'customer'} + + + + + + odex25_helpdesk.ticket.form.inherit.invoicing + odex25_helpdesk.ticket + + + + {"no_create": True} + 0 + + + + + + diff --git a/odex25_project/odex25_helpdesk_timesheet/__init__.py b/odex25_project/odex25_helpdesk_timesheet/__init__.py new file mode 100644 index 000000000..55a65860f --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/__init__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from . import models +from . import wizard + +from odoo import api, SUPERUSER_ID + + +def _odex25_helpdesk_timesheet_post_init(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + teams = env['odex25_helpdesk.team'].search([('use_odex25_helpdesk_timesheet', '=', True), ('project_id', '=', False), ('use_odex25_helpdesk_sale_timesheet', '=', False)]) + + for team in teams: + team.project_id = team._create_project(team.name, team.use_odex25_helpdesk_sale_timesheet, {'allow_timesheets': True, 'allow_timesheets': True}) + env['odex25_helpdesk.ticket'].search([('team_id', '=', team.id), ('project_id', '=', False)]).write({'project_id': team.project_id.id}) diff --git a/odex25_project/odex25_helpdesk_timesheet/__manifest__.py b/odex25_project/odex25_helpdesk_timesheet/__manifest__.py new file mode 100644 index 000000000..2d599487b --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/__manifest__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +{ + 'name': 'Helpdesk Timesheet', + 'category': 'Odex25-Project/Odex25-Project', + 'summary': 'Project, Tasks, Timesheet', + 'author': "Expert Co. Ltd.", + 'website': "http://www.exp-sa.com", + 'depends': ['hr_timesheet', 'odex25_timesheet_grid', 'odex25_helpdesk'], + 'description': """ + - Allow to set project for Helpdesk team + - Track timesheet for a task from a ticket + """, + 'data': [ + 'security/ir.model.access.csv', + 'security/odex25_helpdesk_timesheet_security.xml', + 'views/odex25_helpdesk_views.xml', + 'views/project_views.xml', + 'wizard/odex25_helpdesk_ticket_create_timesheet_views.xml', + 'data/odex25_helpdesk_timesheet_data.xml', + ], + 'demo': ['data/odex25_helpdesk_timesheet_demo.xml'], + 'post_init_hook': '_odex25_helpdesk_timesheet_post_init' +} diff --git a/odex25_project/odex25_helpdesk_timesheet/data/odex25_helpdesk_timesheet_data.xml b/odex25_project/odex25_helpdesk_timesheet/data/odex25_helpdesk_timesheet_data.xml new file mode 100644 index 000000000..df6c9dbd6 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/data/odex25_helpdesk_timesheet_data.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/odex25_project/odex25_helpdesk_timesheet/data/odex25_helpdesk_timesheet_demo.xml b/odex25_project/odex25_helpdesk_timesheet/data/odex25_helpdesk_timesheet_demo.xml new file mode 100644 index 000000000..6bfec5c17 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/data/odex25_helpdesk_timesheet_demo.xml @@ -0,0 +1,25 @@ + + + + + + + + + Fix Drawer Slides + + + 01.00 + + + + + Changed Drawer Handle + + + 0.5 + + + + + diff --git a/odex25_project/odex25_helpdesk_timesheet/i18n/ar.po b/odex25_project/odex25_helpdesk_timesheet/i18n/ar.po new file mode 100644 index 000000000..d3814fe02 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/i18n/ar.po @@ -0,0 +1,404 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * odex25_helpdesk_timesheet +# +# Translators: +# Sherif Abd Ekmoniem , 2020 +# Mustafa Rawi , 2020 +# Akram Alfusayal , 2020 +# amrnegm , 2020 +# Martin Trigaux, 2020 +# Mohammed Ibrahim , 2020 +# Osama Ahmaro , 2020 +# Shaima Safar , 2020 +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-11-27 13:48+0000\n" +"PO-Revision-Date: 2020-09-07 08:20+0000\n" +"Last-Translator: Shaima Safar , 2020\n" +"Language-Team: Arabic (https://www.transifex.com/odoo/teams/41243/ar/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: ar\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket___related_task_ids +msgid " Related Task" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_project_project__ticket_count +msgid "# Tickets" +msgstr "# التذاكر" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_timer_ticket_view_kanban +msgid "" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_timer_ticket_view_kanban +msgid "" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: code:addons/odex25_helpdesk_timesheet/models/odex25_helpdesk.py:0 +#, python-format +msgid "" +"All timesheet hours will be assigned to the selected task on save. Discard " +"to avoid the change." +msgstr "" +"عند الحفظ، سيتم إسناد ساعات سجل الأنشطة للمهمة المختارة. اختر تجاهل لتفادي " +"تطبيق التغيير." + +#. module: odex25_helpdesk_timesheet +#: model:ir.model,name:odex25_helpdesk_timesheet.model_account_analytic_line +msgid "Analytic Line" +msgstr "البند التحليلي" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_create_timesheet_view_form +msgid "Cancel" +msgstr "الغاء" + +#. module: odex25_helpdesk_timesheet +#: code:addons/odex25_helpdesk_timesheet/models/odex25_helpdesk.py:0 +#, python-format +msgid "Closed" +msgstr "مغلق" + +#. module: odex25_helpdesk_timesheet +#: code:addons/odex25_helpdesk_timesheet/models/odex25_helpdesk.py:0 +#, python-format +msgid "Confirm Time Spent" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model,name:odex25_helpdesk_timesheet.model_odex25_helpdesk_ticket_create_timesheet +msgid "Create Timesheet from ticket" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__create_uid +msgid "Created by" +msgstr "أنشئ بواسطة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__create_date +msgid "Created on" +msgstr "أنشئ في" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_create_timesheet_view_form +msgid "Describe your activity..." +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__description +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Description" +msgstr "الوصف" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Description of the ticket..." +msgstr "وصف التذكرة..." + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_account_analytic_line__display_name +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_team__display_name +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__display_name +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__display_name +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_project_project__display_name +msgid "Display Name" +msgstr "الاسم المعروض" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__display_timer +msgid "Display Timer" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__display_timer_pause +msgid "Display Timer Pause" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__display_timer_resume +msgid "Display Timer Resume" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__display_timer_start_primary +msgid "Display Timer Start Primary" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__display_timer_start_secondary +msgid "Display Timer Start Secondary" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__display_timer_stop +msgid "Display Timer Stop" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__display_timesheet_timer +msgid "Display Timesheet Time" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_team__display_timesheet_timer +msgid "Display Timesheet Timer" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_create_timesheet_view_form +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Duration" +msgstr "المدة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__encode_uom_in_days +msgid "Encode Uom In Days" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model,name:odex25_helpdesk_timesheet.model_odex25_helpdesk_team +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_project_project__odex25_helpdesk_team +msgid "Helpdesk Team" +msgstr "فريق مكتب المساعدة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model,name:odex25_helpdesk_timesheet.model_odex25_helpdesk_ticket +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_account_analytic_line__odex25_helpdesk_ticket_id +msgid "Helpdesk Ticket" +msgstr "تذكرة مكتب المساعدة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_account_analytic_line__id +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_team__id +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__id +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__id +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_project_project__id +msgid "ID" +msgstr "المُعرف" + +#. module: odex25_helpdesk_timesheet +#: code:addons/odex25_helpdesk_timesheet/models/odex25_helpdesk.py:0 +#, python-format +msgid "In Progress" +msgstr "قيد التنفيذ" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__is_closed +msgid "Is Closed" +msgstr "مقفل" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__is_task_active +msgid "Is Task Active" +msgstr "هل المهمة نشطة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__is_timer_running +msgid "Is Timer Running" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_account_analytic_line____last_update +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_team____last_update +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket____last_update +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet____last_update +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_project_project____last_update +msgid "Last Modified on" +msgstr "آخر تعديل في" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__write_uid +msgid "Last Updated by" +msgstr "آخر تحديث بواسطة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__write_date +msgid "Last Updated on" +msgstr "آخر تحديث في" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Pause" +msgstr "إيقاف" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model,name:odex25_helpdesk_timesheet.model_project_project +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_team__project_id +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__project_id +msgid "Project" +msgstr "المشروع" + +#. module: odex25_helpdesk_timesheet +#: model:ir.actions.act_window,name:odex25_helpdesk_timesheet.project_project_action_view_odex25_helpdesk_tickets +msgid "Project Tickets" +msgstr "تذاكر المشروع" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,help:odex25_helpdesk_timesheet.field_odex25_helpdesk_team__project_id +msgid "" +"Project to which the tickets (and the timesheets) will be linked by default." +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Resume" +msgstr "استكمال الجلسة" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_create_timesheet_view_form +msgid "Save" +msgstr "حفظ" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_create_timesheet_view_form +msgid "Save time" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Start" +msgstr "بدء" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Stop" +msgstr "إيقاف" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__task_id +msgid "Task" +msgstr "المهمة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,help:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__is_closed +msgid "Tasks in this stage are considered as closed." +msgstr "المهام في هذه المرحلة تعتبر مغلقة." + +#. module: odex25_helpdesk_timesheet +#: code:addons/odex25_helpdesk_timesheet/models/odex25_helpdesk.py:0 +#, python-format +msgid "The project is required to track time on ticket." +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: code:addons/odex25_helpdesk_timesheet/models/odex25_helpdesk.py:0 +#, python-format +msgid "The task must be in ticket's project." +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,help:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__task_id +msgid "The task must have the same customer as this ticket." +msgstr "يجب أن يكون العميل المرتبط بالمهمة هو نفسه العميل صاحب التذكرة." + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.constraint,message:odex25_helpdesk_timesheet.constraint_odex25_helpdesk_ticket_create_timesheet_time_positive +msgid "The timesheet's time must be positive" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,help:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__use_odex25_helpdesk_timesheet +msgid "This required to have project module installed." +msgstr "يتطلب هذا تثبيت موديول المشروع." + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__ticket_id +msgid "Ticket" +msgstr "التذكرة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,help:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__ticket_id +msgid "Ticket for which we are creating a sales order" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_project_project__ticket_ids +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.project_project_view_form_inherit_odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.project_project_view_project_tickets_kanban_inherited +msgid "Tickets" +msgstr "تذاكر" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket_create_timesheet__time_spent +msgid "Time" +msgstr "الوقت" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__timer_pause +msgid "Timer Last Pause" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__timer_start +msgid "Timer Start" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Timesheet Activities" +msgstr "أنشطة سجل النشاط" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_team__timesheet_timer +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__timesheet_timer +msgid "Timesheet Timer" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__use_odex25_helpdesk_timesheet +msgid "Timesheet activated on Team" +msgstr "سجل النشاط المُفعل للفريق" + +#. module: odex25_helpdesk_timesheet +#: code:addons/odex25_helpdesk_timesheet/models/odex25_helpdesk.py:0 +#, python-format +msgid "" +"Timesheet hours will not be assigned to a customer task. Set a task to " +"charge a customer." +msgstr "لن يتم إسناد ساعات سجل الأنشطة لمهمة عميل. اختر مهمة لتُحسب على عميل." + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__timesheet_ids +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Timesheets" +msgstr "سجلات الأنشطة" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__total_hours_spent +msgid "Total Hours Spent" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_ticket_view_form_inherit_odex25_helpdesk_timesheet +msgid "Total hours" +msgstr "إجمالي الساعات" + +#. module: odex25_helpdesk_timesheet +#: model_terms:ir.ui.view,arch_db:odex25_helpdesk_timesheet.odex25_helpdesk_team_view_form_inherit_odex25_helpdesk_timesheet +msgid "Track your time using a timer" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: model:ir.model.fields,field_description:odex25_helpdesk_timesheet.field_odex25_helpdesk_ticket__user_timer_id +msgid "User Timer" +msgstr "" + +#. module: odex25_helpdesk_timesheet +#: code:addons/odex25_helpdesk_timesheet/models/odex25_helpdesk.py:0 +#, python-format +msgid "Warning" +msgstr "تحذير" diff --git a/odex25_project/odex25_helpdesk_timesheet/models/__init__.py b/odex25_project/odex25_helpdesk_timesheet/models/__init__.py new file mode 100644 index 000000000..19e731b0b --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from . import analytic +from . import odex25_helpdesk +from . import project diff --git a/odex25_project/odex25_helpdesk_timesheet/models/analytic.py b/odex25_project/odex25_helpdesk_timesheet/models/analytic.py new file mode 100644 index 000000000..730bfa04a --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/models/analytic.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +from odoo import api, fields, models +from odoo.osv import expression + + +class AccountAnalyticLine(models.Model): + _inherit = 'account.analytic.line' + + odex25_helpdesk_ticket_id = fields.Many2one('odex25_helpdesk.ticket', 'Helpdesk Ticket') + + def _compute_task_id(self): + super(AccountAnalyticLine, self)._compute_task_id() + for line in self.filtered(lambda line: line.odex25_helpdesk_ticket_id): + line.task_id = line.odex25_helpdesk_ticket_id.task_id + + def _timesheet_preprocess(self, vals): + odex25_helpdesk_ticket_id = vals.get('odex25_helpdesk_ticket_id') + if odex25_helpdesk_ticket_id: + ticket = self.env['odex25_helpdesk.ticket'].browse(odex25_helpdesk_ticket_id) + if ticket.project_id: + vals['project_id'] = ticket.project_id.id + if ticket.task_id: + vals['task_id'] = ticket.task_id.id + vals = super(AccountAnalyticLine, self)._timesheet_preprocess(vals) + return vals + + def _timesheet_get_portal_domain(self): + domain = super(AccountAnalyticLine, self)._timesheet_get_portal_domain() + return expression.OR([domain, self._timesheet_in_odex25_helpdesk_get_portal_domain()]) + + def _timesheet_in_odex25_helpdesk_get_portal_domain(self): + return [ + '&', + '&', + '&', + ('task_id', '=', False), + ('odex25_helpdesk_ticket_id', '!=', False), + '|', + '|', + ('project_id.message_partner_ids', 'child_of', [self.env.user.partner_id.commercial_partner_id.id]), + ('project_id.allowed_portal_user_ids', 'child_of', [self.env.user.id]), + ('odex25_helpdesk_ticket_id.message_partner_ids', 'child_of', [self.env.user.partner_id.commercial_partner_id.id]), + ('project_id.privacy_visibility', '=', 'portal') + ] diff --git a/odex25_project/odex25_helpdesk_timesheet/models/odex25_helpdesk.py b/odex25_project/odex25_helpdesk_timesheet/models/odex25_helpdesk.py new file mode 100644 index 000000000..827a00f90 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/models/odex25_helpdesk.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +from math import ceil + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class odex25_helpdeskTeam(models.Model): + _inherit = 'odex25_helpdesk.team' + + project_id = fields.Many2one("project.project", string="Project", ondelete="restrict", domain="[('allow_timesheets', '=', True), ('company_id', '=', company_id)]", + help="Project to which the tickets (and the timesheets) will be linked by default.") + timesheet_timer = fields.Boolean('Timesheet Timer', default=True) + display_timesheet_timer = fields.Boolean(compute='_compute_display_timesheet_timer') + + @api.depends('use_odex25_helpdesk_timesheet') + def _compute_display_timesheet_timer(self): + is_uom_hour = self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_hour') + for team in self: + team.display_timesheet_timer = team.use_odex25_helpdesk_timesheet and is_uom_hour + + @api.depends('use_odex25_helpdesk_timesheet') + def _compute_timesheet_timer(self): + for team in self: + team.timesheet_timer = team.use_odex25_helpdesk_timesheet + + def _create_project(self, name, allow_billable, other): + return self.env['project.project'].create({ + 'name': name, + 'type_ids': [ + (0, 0, {'name': _('In Progress')}), + (0, 0, {'name': _('Closed'), 'is_closed': True}) + ], + 'allow_timesheets': True, + 'allow_timesheet_timer': True, + **other, + }) + + @api.model + def create(self, vals): + if vals.get('use_odex25_helpdesk_timesheet') and not vals.get('project_id'): + allow_billable = vals.get('use_odex25_helpdesk_sale_timesheet') + vals['project_id'] = self._create_project(vals['name'], allow_billable, {}).id + return super(odex25_helpdeskTeam, self).create(vals) + + def write(self, vals): + if 'use_odex25_helpdesk_timesheet' in vals and not vals['use_odex25_helpdesk_timesheet']: + vals['project_id'] = False + result = super(odex25_helpdeskTeam, self).write(vals) + for team in self.filtered(lambda team: team.use_odex25_helpdesk_timesheet and not team.project_id): + team.project_id = team._create_project(team.name, team.use_odex25_helpdesk_sale_timesheet, {'allow_timesheets': True, 'allow_timesheet_timer': True}) + self.env['odex25_helpdesk.ticket'].search([('team_id', '=', team.id), ('project_id', '=', False)]).write({'project_id': team.project_id.id}) + return result + + @api.model + def _init_data_create_project(self): + # TODO: remove me in master + return + + +class odex25_helpdeskTicket(models.Model): + _inherit = 'odex25_helpdesk.ticket' + # _inherit = ['odex25_helpdesk.ticket', 'timer.mixin'] + + @api.model + def default_get(self, fields_list): + result = super(odex25_helpdeskTicket, self).default_get(fields_list) + if 'project_id' in fields_list and result.get('team_id') and not result.get('project_id'): + result['project_id'] = self.env['odex25_helpdesk.team'].browse(result['team_id']).project_id.id + return result + + # TODO: [XBO] change this field in related and stored (to count the number of tickets per project) field to the one in odex25_helpdesk.team + project_id = fields.Many2one("project.project", string="Project", domain="[('allow_timesheets', '=', True), ('company_id', '=', company_id)]") + # TODO: [XBO] remove me in master + task_id = fields.Many2one( + "project.task", string="Task", compute='_compute_task_id', store=True, readonly=False, + domain="[('id', 'in', _related_task_ids)]", tracking=True, + help="The task must have the same customer as this ticket.") + # TODO: [XBO] remove me in master (since task_id field will be removed too) + _related_task_ids = fields.Many2many('project.task', compute='_compute_related_task_ids') + timesheet_ids = fields.One2many('account.analytic.line', 'odex25_helpdesk_ticket_id', 'Timesheets') + is_closed = fields.Boolean(related="task_id.stage_id.is_closed", string="Is Closed", readonly=True) + # TODO: [XBO] remove me in master (since task_id field will be removed too) + is_task_active = fields.Boolean(related="task_id.active", string='Is Task Active', readonly=True) + use_odex25_helpdesk_timesheet = fields.Boolean('Timesheet activated on Team', related='team_id.use_odex25_helpdesk_timesheet', readonly=True) + timesheet_timer = fields.Boolean(related='team_id.timesheet_timer') + display_timesheet_timer = fields.Boolean("Display Timesheet Time", compute='_compute_display_timesheet_timer') + total_hours_spent = fields.Float(compute='_compute_total_hours_spent', default=0) + display_timer_start_secondary = fields.Boolean(compute='_compute_display_timer_buttons') + display_timer = fields.Boolean(compute='_compute_display_timer') + encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days') + + def _compute_encode_uom_in_days(self): + self.encode_uom_in_days = self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day') + + @api.depends('display_timesheet_timer', 'timer_start', 'timer_pause', 'total_hours_spent') + def _compute_display_timer_buttons(self): + for ticket in self: + if not ticket.display_timesheet_timer: + ticket.update({ + 'display_timer_start_primary': False, + 'display_timer_start_secondary': False, + 'display_timer_stop': False, + 'display_timer_pause': False, + 'display_timer_resume': False, + }) + else: + super(odex25_helpdeskTicket, ticket)._compute_display_timer_buttons() + ticket.display_timer_start_secondary = ticket.display_timer_start_primary + if not ticket.timer_start: + ticket.update({ + 'display_timer_stop': False, + 'display_timer_pause': False, + 'display_timer_resume': False, + }) + if not ticket.total_hours_spent: + ticket.display_timer_start_secondary = False + else: + ticket.display_timer_start_primary = False + + def _compute_display_timer(self): + if self.env.user.has_group('odex25_helpdesk.group_odex25_helpdesk_user') and self.env.user.has_group('hr_timesheet.group_hr_timesheet_user'): + self.display_timer = True + else: + self.display_timer = False + + @api.depends('use_odex25_helpdesk_timesheet', 'timesheet_timer', 'timesheet_ids', 'encode_uom_in_days') + def _compute_display_timesheet_timer(self): + for ticket in self: + ticket.display_timesheet_timer = ticket.use_odex25_helpdesk_timesheet and ticket.timesheet_timer and not ticket.encode_uom_in_days + + @api.depends('project_id', 'company_id') + def _compute_related_task_ids(self): + # TODO: [XBO] remove me in master because the task_id will be removed, then this compute and the _related_task_ids field will be useless + for t in self: + domain = [('project_id.allow_timesheets', '=', True), ('company_id', '=', t.company_id.id)] + if t.project_id: + domain = [('project_id', '=', t.project_id.id)] + t._related_task_ids = self.env['project.task'].search(domain)._origin + + @api.depends('timesheet_ids') + def _compute_total_hours_spent(self): + for ticket in self: + ticket.total_hours_spent = round(sum(ticket.timesheet_ids.mapped('unit_amount')), 2) + + @api.depends('project_id') + def _compute_task_id(self): + # TODO: [XBO] remove me in master (task_id field will be removed) + with_different_project = self.filtered(lambda t: t.project_id != t.task_id.project_id) + with_different_project.update({'task_id': False}) + + @api.onchange('task_id') + def _onchange_task_id(self): + # TODO: remove me in master + return + + @api.constrains('project_id', 'team_id') + def _check_project_id(self): + # TODO: [XBO] see in master if we must remove this method, but since project_id will be a related field, this constrains will be useless. + for ticket in self: + if ticket.use_odex25_helpdesk_timesheet and not ticket.project_id: + raise ValidationError(_("The project is required to track time on ticket.")) + + @api.constrains('project_id', 'task_id') + def _check_task_in_project(self): + # TODO: [XBO] remove me in master (task_id field will be removed in master) + for ticket in self: + if ticket.task_id: + if ticket.task_id.project_id != ticket.project_id: + raise ValidationError(_("The task must be in ticket's project.")) + + def _get_timesheet(self): + # return not invoiced timesheet + timesheet_ids = self.timesheet_ids + return timesheet_ids.filtered(lambda t: (not t.timesheet_invoice_id or t.timesheet_invoice_id.state == 'cancel')) + + @api.model_create_multi + def create(self, value_list): + team_ids = set([value['team_id'] for value in value_list if value.get('team_id')]) + teams = self.env['odex25_helpdesk.team'].browse(team_ids) + + team_project_map = {} # map with the team that require a project + for team in teams: + if team.use_odex25_helpdesk_timesheet: + team_project_map[team.id] = team.project_id.id + + for value in value_list: + if value.get('team_id') and not value.get('project_id') and team_project_map.get(value['team_id']): + value['project_id'] = team_project_map[value['team_id']] + + return super(odex25_helpdeskTicket, self).create(value_list) + + def write(self, values): + result = super(odex25_helpdeskTicket, self).write(values) + # force timesheet values: changing ticket's task or project will reset timesheet ones + timesheet_vals = {} + for fname in self._timesheet_forced_fields(): + if fname in values: + timesheet_vals[fname] = values[fname] + if timesheet_vals: + for timesheet in self.sudo()._get_timesheet(): + timesheet.write(timesheet_vals) # sudo since Helpdesk user can change task + return result + +# @api.model +# def _fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): +# """ Set the correct label for `unit_amount`, depending on company UoM """ +# result = super(odex25_helpdeskTicket, self)._fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) +# result['arch'] = self.env['account.analytic.line']._apply_timesheet_label(result['arch']) +# return result + + def action_view_ticket_task(self): + # TODO: [XBO] remove me in master (task_id field will be removed in master) + self.ensure_one() + return { + 'view_mode': 'form', + 'res_model': 'project.task', + 'type': 'ir.actions.act_window', + 'res_id': self.task_id.id, + } + + def _timesheet_forced_fields(self): + """ return the list of field that should also be written on related timesheets """ + return ['task_id', 'project_id'] + + def action_timer_start(self): + if not self.user_timer_id.timer_start and self.display_timesheet_timer: + super().action_timer_start() + + def action_timer_stop(self): + # timer was either running or paused + if self.user_timer_id.timer_start and self.display_timesheet_timer: + minutes_spent = self.user_timer_id._get_minutes_spent() + minimum_duration = int(self.env['ir.config_parameter'].sudo().get_param('hr_timesheet.timesheet_min_duration', 0)) + rounding = int(self.env['ir.config_parameter'].sudo().get_param('hr_timesheet.timesheet_rounding', 0)) + minutes_spent = self._timer_rounding(minutes_spent, minimum_duration, rounding) + return self._action_open_new_timesheet(minutes_spent * 60 / 3600) + return False + + def _action_open_new_timesheet(self, time_spent): + return { + "name": _("Confirm Time Spent"), + "type": 'ir.actions.act_window', + "res_model": 'odex25_helpdesk.ticket.create.timesheet', + "views": [[False, "form"]], + "target": 'new', + "context": { + **self.env.context, + 'active_id': self.id, + 'active_model': self._name, + 'default_time_spent': time_spent, + }, + } diff --git a/odex25_project/odex25_helpdesk_timesheet/models/project.py b/odex25_project/odex25_helpdesk_timesheet/models/project.py new file mode 100644 index 000000000..6b7b58de3 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/models/project.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from odoo import api, fields, models + +class Project(models.Model): + _inherit = 'project.project' + + ticket_ids = fields.One2many('odex25_helpdesk.ticket', 'project_id', string='Tickets') + ticket_count = fields.Integer('# Tickets', compute='_compute_ticket_count') + + odex25_helpdesk_team = fields.One2many('odex25_helpdesk.team', 'project_id') + + @api.depends('ticket_ids.project_id') + def _compute_ticket_count(self): + if not self.user_has_groups('odex25_helpdesk.group_odex25_helpdesk_user'): + self.ticket_count = 0 + return + result = self.env['odex25_helpdesk.ticket'].read_group([ + ('project_id', 'in', self.ids) + ], ['project_id'], ['project_id']) + data = {data['project_id'][0]: data['project_id_count'] for data in result} + for project in self: + project.ticket_count = data.get(project.id, 0) + + @api.depends('odex25_helpdesk_team.timesheet_timer') + def _compute_allow_timesheet_timer(self): + super(Project, self)._compute_allow_timesheet_timer() + + for project in self: + project.allow_timesheet_timer = project.allow_timesheet_timer or project.odex25_helpdesk_team.timesheet_timer diff --git a/odex25_project/odex25_helpdesk_timesheet/security/ir.model.access.csv b/odex25_project/odex25_helpdesk_timesheet/security/ir.model.access.csv new file mode 100644 index 000000000..4a3d31c32 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/security/ir.model.access.csv @@ -0,0 +1,6 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_project_project_odex25_helpdesk_user,project.project.odex25_helpdesk.user,project.model_project_project,odex25_helpdesk.group_odex25_helpdesk_user,1,0,0,0 +access_project_task_odex25_helpdesk_user,project.task.odex25_helpdesk.user,project.model_project_task,odex25_helpdesk.group_odex25_helpdesk_user,1,0,0,0 +access_analytic_account_heldpdesk_user,analytic.account.odex25_helpdesk.user,analytic.model_account_analytic_account,odex25_helpdesk.group_odex25_helpdesk_user,1,1,0,0 +access_analytic_line_heldpdesk_user,analytic.line.odex25_helpdesk.user,analytic.model_account_analytic_line,odex25_helpdesk.group_odex25_helpdesk_user,1,1,1,1 +access_odex25_helpdesk_ticket_create_timesheet,access.odex25_helpdesk.ticket.create.timesheet,model_odex25_helpdesk_ticket_create_timesheet,odex25_helpdesk.group_odex25_helpdesk_user,1,1,1,0 diff --git a/odex25_project/odex25_helpdesk_timesheet/security/odex25_helpdesk_timesheet_security.xml b/odex25_project/odex25_helpdesk_timesheet/security/odex25_helpdesk_timesheet_security.xml new file mode 100644 index 000000000..29233bfed --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/security/odex25_helpdesk_timesheet_security.xml @@ -0,0 +1,26 @@ + + + + + account.analytic.line.odex25_helpdesk.user + + [('user_id', '=', user.id), ('odex25_helpdesk_ticket_id', '!=', False)] + + + + + + + + + account.analytic.line.timesheet.manager + + [('odex25_helpdesk_ticket_id', '!=', False)] + + + + + + + + \ No newline at end of file diff --git a/odex25_project/odex25_helpdesk_timesheet/static/description/icon.png b/odex25_project/odex25_helpdesk_timesheet/static/description/icon.png new file mode 100644 index 000000000..4141f52da Binary files /dev/null and b/odex25_project/odex25_helpdesk_timesheet/static/description/icon.png differ diff --git a/odex25_project/odex25_helpdesk_timesheet/tests/__init__.py b/odex25_project/odex25_helpdesk_timesheet/tests/__init__.py new file mode 100644 index 000000000..a39e41c3e --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import test_project + diff --git a/odex25_project/odex25_helpdesk_timesheet/tests/test_project.py b/odex25_project/odex25_helpdesk_timesheet/tests/test_project.py new file mode 100644 index 000000000..6fef5bd4c --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/tests/test_project.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +from odoo.tests.common import TransactionCase + + +class TestTimesheet(TransactionCase): + + def setUp(self): + super(TestTimesheet, self).setUp() + + self.partner = self.env['res.partner'].create({ + 'name': 'Customer Task', + 'email': 'customer@task.com', + }) + + self.analytic_account = self.env['account.analytic.account'].create({ + 'name': 'Analytic Account for Test Customer', + 'partner_id': self.partner.id, + 'code': 'TEST' + }) + + def test_allow_timesheets_and_timer(self): + """ + Check that a modification of the 'allow_timesheets' field updates correctly the + 'allow_timesheet_timer' field. + """ + Project = self.env['project.project'] + odex25_helpdeskTeam = self.env['odex25_helpdesk.team'] + + # case 1: create a project with allow_timesheets set to FALSE + project_1 = Project.create({ + 'name': 'Project 1', + 'allow_timesheets': False, + 'partner_id': self.partner.id + }) + + self.assertFalse( + project_1.allow_timesheet_timer, + "On project creation with 'allow_timesheets' set to FALSE, 'allow_timesheet_timer' shall be set to FALSE") + + # case 2: create a project with allow_timesheets set to TRUE + project_2 = Project.create({ + 'name': 'Project 2', + 'allow_timesheets': True, + 'partner_id': self.partner.id, + 'analytic_account_id': self.analytic_account.id + }) + + self.assertTrue( + project_2.allow_timesheet_timer, + "On project creation with 'allow_timesheets' set to TRUE, 'allow_timesheet_timer' shall be set to TRUE") + + # case 3: change 'allow_timesheets' from FALSE to TRUE + project_1.write({ + 'allow_timesheets': True + }) + + self.assertTrue( + project_1.allow_timesheet_timer, + "On 'allow_timesheets' change to TRUE, 'allow_timesheet_timer' shall be set to TRUE") + + # case 4: change 'allow_timesheets' from TRUE to FALSE + project_2.write({ + 'allow_timesheets': False + }) + + self.assertFalse( + project_2.allow_timesheet_timer, + "On 'allow_timesheets' change to FALSE, 'allow_timesheet_timer' shall be set to FALSE") + + # case 5: a Helpdesk team without timesheet timer + odex25_helpdeskTeam_1 = odex25_helpdeskTeam.create({ + 'name': 'Team #1', + 'project_id': project_1.id, + 'timesheet_timer': False, + }) + + self.assertTrue( + odex25_helpdeskTeam_1.project_id.allow_timesheet_timer, + "If 'allow_timesheets' is TRUE and 'timesheet_timer' is FALSE, 'allow_timesheet_timer' shall be set to TRUE") + + # case 5: a Helpdesk team with a timesheet timer + odex25_helpdeskTeam_2 = odex25_helpdeskTeam.create({ + 'name': 'Team #2', + 'project_id': project_2.id, + 'timesheet_timer': True, + }) + + self.assertTrue( + odex25_helpdeskTeam_2.project_id.allow_timesheet_timer, + "If 'allow_timesheets' is FALSE and 'timesheet_timer' is TRUE," + " 'allow_timesheet_timer' shall be set to TRUE") + + # case 6: project with 'allow_timesheets' set to FALSE and team with + # 'timesheet_timer' set to FALSE. + project_3 = Project.create({ + 'name': 'Project 3', + 'allow_timesheets': False, + 'partner_id': self.partner.id + }) + + odex25_helpdeskTeam_3 = odex25_helpdeskTeam.create({ + 'name': 'Team #3', + 'project_id': project_3.id, + 'timesheet_timer': False, + }) + + self.assertFalse( + odex25_helpdeskTeam_3.project_id.allow_timesheet_timer, + "If 'allow_timesheets' is FALSE and 'timesheet_timer' is FALSE," + " 'allow_timesheet_timer' shall be set to FALSE") diff --git a/odex25_project/odex25_helpdesk_timesheet/views/odex25_helpdesk_views.xml b/odex25_project/odex25_helpdesk_timesheet/views/odex25_helpdesk_views.xml new file mode 100644 index 000000000..85460e0b5 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/views/odex25_helpdesk_views.xml @@ -0,0 +1,189 @@ + + + + + odex25_helpdesk.team.form.inherit.timesheet + odex25_helpdesk.team + + + +
+
+
+ + +
+
+ +
+
+
+
+
+
+
+ + + odex25_helpdesk.ticket.form.inherit.timesheet + odex25_helpdesk.ticket + + + + + ml-2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +
+
+
+
+ +
+
+ + + +
+
+
+
+
+
+
+ + + + + + + + + + + +
+
+ + + + + + + +
+
+
+
+
+ + + odex25_helpdesk.ticket.search.inherit.timesheet + odex25_helpdesk.ticket + + + + + + + + + + + odex25_helpdesk.ticket.kanban.timer + odex25_helpdesk.ticket + 5 + + + + + + + + + + + + + + + +
diff --git a/odex25_project/odex25_helpdesk_timesheet/views/project_views.xml b/odex25_project/odex25_helpdesk_timesheet/views/project_views.xml new file mode 100644 index 000000000..15d3024a1 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/views/project_views.xml @@ -0,0 +1,47 @@ + + + + + Project Tickets + odex25_helpdesk.ticket + kanban,tree,form,pivot,graph + + {'search_default_project_id': active_id, 'default_project_id': active_id} + + + + project.project.tickets.kanban.inherited + project.project + + 24 + + + + + + + + + + + + project.form.inherit.odex25_helpdesk.timesheet + project.project + + +
+ +
+
+ +
+ +
diff --git a/odex25_project/odex25_helpdesk_timesheet/wizard/__init__.py b/odex25_project/odex25_helpdesk_timesheet/wizard/__init__.py new file mode 100644 index 000000000..2ba46a4a2 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/wizard/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import odex25_helpdesk_ticket_create_timesheet diff --git a/odex25_project/odex25_helpdesk_timesheet/wizard/odex25_helpdesk_ticket_create_timesheet.py b/odex25_project/odex25_helpdesk_timesheet/wizard/odex25_helpdesk_ticket_create_timesheet.py new file mode 100644 index 000000000..aff87ba79 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/wizard/odex25_helpdesk_ticket_create_timesheet.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +from odoo import api, fields, models + + +class odex25_helpdeskTicketCreateTimesheet(models.TransientModel): + _name = 'odex25_helpdesk.ticket.create.timesheet' + _description = "Create Timesheet from ticket" + + _sql_constraints = [('time_positive', 'CHECK(time_spent > 0)', "The timesheet's time must be positive" )] + + time_spent = fields.Float('Time', digits=(16, 2)) + description = fields.Char('Description') + ticket_id = fields.Many2one( + 'odex25_helpdesk.ticket', "Ticket", required=True, + default=lambda self: self.env.context.get('active_id', None), + help="Ticket for which we are creating a sales order", + ) + + def action_generate_timesheet(self): + values = { + 'task_id': self.ticket_id.task_id.id, + 'project_id': self.ticket_id.project_id.id, + 'date': fields.Datetime.now(), + 'name': self.description, + 'user_id': self.env.uid, + 'unit_amount': self.time_spent, + } + + timesheet = self.env['account.analytic.line'].create(values) + + self.ticket_id.write({ + 'timer_start': False, + 'timer_pause': False + }) + self.ticket_id.timesheet_ids = [(4, timesheet.id, None)] + self.ticket_id.user_timer_id.unlink() + return timesheet diff --git a/odex25_project/odex25_helpdesk_timesheet/wizard/odex25_helpdesk_ticket_create_timesheet_views.xml b/odex25_project/odex25_helpdesk_timesheet/wizard/odex25_helpdesk_ticket_create_timesheet_views.xml new file mode 100644 index 000000000..c4738a0a7 --- /dev/null +++ b/odex25_project/odex25_helpdesk_timesheet/wizard/odex25_helpdesk_ticket_create_timesheet_views.xml @@ -0,0 +1,22 @@ + + + + + odex25_helpdesk.ticket.create.timesheet.wizard.form + odex25_helpdesk.ticket.create.timesheet + +
+ + + + + +
+
+
+
+
+ +
diff --git a/odex25_project/project_base/.idea/inspectionProfiles/profiles_settings.xml b/odex25_project/project_base/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 000000000..105ce2da2 --- /dev/null +++ b/odex25_project/project_base/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/odex25_project/project_base/.idea/misc.xml b/odex25_project/project_base/.idea/misc.xml new file mode 100644 index 000000000..2d83d70ff --- /dev/null +++ b/odex25_project/project_base/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/odex25_project/project_base/.idea/modules.xml b/odex25_project/project_base/.idea/modules.xml new file mode 100644 index 000000000..d6d1f7c82 --- /dev/null +++ b/odex25_project/project_base/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/odex25_project/project_base/.idea/project_base.iml b/odex25_project/project_base/.idea/project_base.iml new file mode 100644 index 000000000..d0876a78d --- /dev/null +++ b/odex25_project/project_base/.idea/project_base.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/odex25_project/project_base/.idea/workspace.xml b/odex25_project/project_base/.idea/workspace.xml new file mode 100644 index 000000000..33a513015 --- /dev/null +++ b/odex25_project/project_base/.idea/workspace.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + 1715529731411 + + + + \ No newline at end of file diff --git a/odex25_project/project_base/__init__.py b/odex25_project/project_base/__init__.py new file mode 100644 index 000000000..f2155f77c --- /dev/null +++ b/odex25_project/project_base/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from . import models +from . import wizard + diff --git a/odex25_project/project_base/__manifest__.py b/odex25_project/project_base/__manifest__.py new file mode 100644 index 000000000..549eabee9 --- /dev/null +++ b/odex25_project/project_base/__manifest__.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Odex25 Project Base", + 'summary': """Odex25 Project Base""", + 'description': """ +1- Project Charter +2- Project Stage +3- Project Tasks +4- Project Invoice Requests + """, + 'category': 'Odex25-Project/Odex25-Project', + 'version': '1.0', + 'depends': ['sale_timesheet','hr' ,'project','mail', 'portal', 'base' ,'account'], + 'data': [ + 'data/project_data.xml', + 'data/project_cron.xml', + 'security/project_security.xml', + 'security/ir_rule_allow_users.xml', + 'security/ir.model.access.csv', + 'wizard/project_hold_reason_view.xml', + 'wizard/edit_project_phase_view.xml', + 'wizard/down_payment_invoice_advance_views.xml', + 'report/project_reports.xml', + 'report/project_report_templates.xml', + 'report/project_invoice_report_templates.xml', + 'views/project_views.xml', + 'views/project_invoice_views.xml', + 'views/project_phase_view.xml', + 'views/res_config_setting.xml', + 'views/project_task_views.xml', + 'views/asset.xml', + + ], +} diff --git a/odex25_project/project_base/data/project_cron.xml b/odex25_project/project_base/data/project_cron.xml new file mode 100644 index 000000000..1715fd934 --- /dev/null +++ b/odex25_project/project_base/data/project_cron.xml @@ -0,0 +1,26 @@ + + + + Project: Update Project Status + 1 + days + -1 + + + + model.update_project_status() + code + + + + Project: Send Invoice Issue Notification + 1 + days + -1 + + + + model.project_invoice_notification() + code + + diff --git a/odex25_project/project_base/data/project_data.xml b/odex25_project/project_base/data/project_data.xml new file mode 100644 index 000000000..c2489b81d --- /dev/null +++ b/odex25_project/project_base/data/project_data.xml @@ -0,0 +1,16 @@ + + + + + + Project Number + project.project + P%(y)s- + 5 + + + + + diff --git a/odex25_project/project_base/i18n/ar.po b/odex25_project/project_base/i18n/ar.po new file mode 100644 index 000000000..b4a7497b9 --- /dev/null +++ b/odex25_project/project_base/i18n/ar.po @@ -0,0 +1,48 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-05-11 15:00+0000\n" +"PO-Revision-Date: 2024-05-11 15:00+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: project +#: model:ir.actions.act_window,name:project.open_view_project_all +#: model:ir.actions.act_window,name:project.open_view_project_all_config +#: model:ir.model.fields,field_description:project.field_account_analytic_account__project_ids +#: model:ir.model.fields,field_description:project.field_project_delete_wizard__project_ids +#: model:ir.model.fields,field_description:project.field_project_task_type__project_ids +#: model:ir.model.fields,field_description:project.field_project_task_type_delete_wizard__project_ids +#: model:ir.ui.menu,name:project.menu_projects +#: model:ir.ui.menu,name:project.menu_projects_config +#: model_terms:ir.ui.view,arch_db:project.account_analytic_account_view_form_inherit +#: model_terms:ir.ui.view,arch_db:project.portal_layout +#: model_terms:ir.ui.view,arch_db:project.portal_my_home +#: model_terms:ir.ui.view,arch_db:project.portal_my_projects +#: model_terms:ir.ui.view,arch_db:project.view_project +msgid "Projects" +msgstr "المشاريع" + +#. module: project +#: model:ir.ui.menu,name:project.menu_main_pm +msgid "Project" +msgstr "المشروع" + +#. module: project +#: model_terms:ir.ui.view,arch_db:project.edit_project +msgid "Tasks In Progress" +msgstr "المهام قيد التنفيذ" + +#. module: project_base +#: model:ir.model.fields,field_description:project_base.field_project_task__task_progress +msgid "Task Progress" +msgstr "نسبة الانجاز" diff --git a/odex25_project/project_base/i18n/ar_001.po b/odex25_project/project_base/i18n/ar_001.po new file mode 100644 index 000000000..e7ada4c5a --- /dev/null +++ b/odex25_project/project_base/i18n/ar_001.po @@ -0,0 +1,2142 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_base +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-03 08:58+0000\n" +"PO-Revision-Date: 2024-06-03 08:58+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: project_base +#: code:addons/project_base/models/project_phase.py:0 +#, python-format +msgid " For Reasone: %s" +msgstr "" + +#. module: project_base +#: code:addons/project_base/wizard/project_hold_reason.py:0 +#, python-format +msgid " with Reason: %s" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.view_edit_project_phase_wizard +msgid "" +"\n" +" Kindly make sure to Save current project stages before edit.\n" +" " +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "Country" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "Customer" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "Finish Date" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "Kick-Off Date" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "Project Category" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "Project Manager" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "Sale Order" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "Start Date" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.project_detail_qweb_report +msgid "stage" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.view_new_project_kanban +msgid "" +msgstr "" + +#. module: project_base +#: model_terms:ir.ui.view,arch_db:project_base.view_edit_project_inherit_form +msgid "" +"\n" +" Project Phase updated %s