From e01057753e034b39dcab3588d239f77f1269dec9 Mon Sep 17 00:00:00 2001 From: younes Date: Sun, 4 May 2025 07:29:42 +0100 Subject: [PATCH] Add Employee Custody Report under Purchase Orders report --- .../purchase_custom_report/__manifest__.py | 4 +- .../purchase_custom_report/i18n/ar_001.po | 76 +++- .../reports/employee_custody_report.xml | 87 +++++ .../reports/report_actions.xml | 8 + .../security/ir.model.access.csv | 1 + .../purchase_custom_report/wizard/__init__.py | 1 + .../wizard/employee_custody_report_wizard.py | 75 ++++ .../wizard/employee_custody_report_wizard.xml | 44 +++ .../purchase_custom_stock/__manifest__.py | 4 +- .../purchase_custom_stock/i18n/ar_001.po | 90 ++++- .../purchase_custom_stock/models/__init__.py | 3 + .../models/account_asset_operation.py | 8 + .../models/employee_custody.py | 13 + .../models/product_template.py | 9 + .../models/purchase_request.py | 355 +++++++++++------- .../views/account_asset_operation.xml | 13 + .../views/product_template.xml | 16 + .../views/purchase_request.xml | 92 ++++- .../wizards/picking_purchase_request.py | 104 ++--- 19 files changed, 810 insertions(+), 193 deletions(-) create mode 100644 odex25_purchase/purchase_custom_report/reports/employee_custody_report.xml create mode 100644 odex25_purchase/purchase_custom_report/wizard/employee_custody_report_wizard.py create mode 100644 odex25_purchase/purchase_custom_report/wizard/employee_custody_report_wizard.xml create mode 100644 odex25_purchase/purchase_custom_stock/models/account_asset_operation.py create mode 100644 odex25_purchase/purchase_custom_stock/models/employee_custody.py create mode 100644 odex25_purchase/purchase_custom_stock/models/product_template.py create mode 100644 odex25_purchase/purchase_custom_stock/views/account_asset_operation.xml create mode 100644 odex25_purchase/purchase_custom_stock/views/product_template.xml diff --git a/odex25_purchase/purchase_custom_report/__manifest__.py b/odex25_purchase/purchase_custom_report/__manifest__.py index a4f8e57a8..2fa3c0937 100644 --- a/odex25_purchase/purchase_custom_report/__manifest__.py +++ b/odex25_purchase/purchase_custom_report/__manifest__.py @@ -20,8 +20,10 @@ 'views/asset_custom.xml', 'wizard/purchase_committee_report.xml', 'reports/purchase_committe_report.xml', + 'wizard/employee_custody_report_wizard.xml', + 'reports/employee_custody_report.xml', 'views/backend_assets.xml', ], - 'depends' : ['purchase_requisition_custom','hr'], + 'depends' : ['purchase_requisition_custom','hr','purchase_custom_stock'], 'installable': True, 'application': True, } diff --git a/odex25_purchase/purchase_custom_report/i18n/ar_001.po b/odex25_purchase/purchase_custom_report/i18n/ar_001.po index d42b84be6..be5fe9b47 100644 --- a/odex25_purchase/purchase_custom_report/i18n/ar_001.po +++ b/odex25_purchase/purchase_custom_report/i18n/ar_001.po @@ -56,6 +56,16 @@ msgstr "الاجمالي:" msgid "Approved" msgstr "معتمد" +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Asset" +msgstr "الاصل" + +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Assignment Date" +msgstr "تاريخ الاسناد" + #. module: purchase_custom_report #: code:addons/purchase_custom_report/wizard/purchase_wizard.py:0 #: model:ir.model.fields.selection,name:purchase_custom_report.selection__purchase_committee_report__state__open @@ -84,6 +94,7 @@ msgstr "مراجعة الميزانية" #: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_purchase_report_wizard_form5 #: model_terms:ir.ui.view,arch_db:purchase_custom_report.model_name_view_form #: model_terms:ir.ui.view,arch_db:purchase_custom_report.purchase_committe_report_form +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.wizard_employee_custody_report_form #: model_terms:ir.ui.view,arch_db:purchase_custom_report.purchase_general_report_wizard_form #: model_terms:ir.ui.view,arch_db:purchase_custom_report.wizard_purchase_order_report_form #: model_terms:ir.ui.view,arch_db:purchase_custom_report.wizard_purchase_requisition_form @@ -167,6 +178,16 @@ msgstr "مؤكد" msgid "Created by" msgstr "انشيء بواسطة" +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Custody Period" +msgstr "فترة العهدة" + +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Custody Type" +msgstr "نوع العهدة" + #. module: purchase_custom_report #: model:ir.model.fields,field_description:purchase_custom_report.field_employee_purchase_report__create_date #: model:ir.model.fields,field_description:purchase_custom_report.field_employee_purchase_requistion__create_date @@ -198,6 +219,11 @@ msgstr "تاريخ" msgid "Date From" msgstr "بداية من" +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Date From :" +msgstr "تاريخ البداية:" + #. module: purchase_custom_report #: code:addons/purchase_custom_report/wizard/purchase_wizard.py:0 #: code:addons/purchase_custom_report/wizard/purchase_wizard.py:0 @@ -215,6 +241,11 @@ msgstr "تاريخ البداية يجب ان يكون قبل تاريخ الن msgid "Date To" msgstr "نهاية الي" +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Date To:" +msgstr "تاريخ النهاية:" + #. module: purchase_custom_report #: model_terms:ir.ui.view,arch_db:purchase_custom_report.purchase_comittee_report_details msgid "Date:" @@ -230,6 +261,7 @@ msgid "Deparments" msgstr "اقسام" #. module: purchase_custom_report +#: model:ir.model.fields,field_description:purchase_custom_report.field_employee_custody_report__department_id #: model:ir.model.fields,field_description:purchase_custom_report.field_purchase_order_report_wizard__department_id #: model:ir.model.fields,field_description:purchase_custom_report.field_purchase_requisition_wizard__department_id #: model:ir.model.fields,field_description:purchase_custom_report.field_vendor_report_wizard__department_id @@ -243,10 +275,11 @@ msgid "Department" msgstr "قسم" #. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details #: model_terms:ir.ui.view,arch_db:purchase_custom_report.purchase_comittee_report_details #: model_terms:ir.ui.view,arch_db:purchase_custom_report.purchase_general_report msgid "Department:" -msgstr "قسم" +msgstr "قسم:" #. module: purchase_custom_report #: model:ir.model.fields.selection,name:purchase_custom_report.selection__employee_purchase_report__type__detailed @@ -304,6 +337,7 @@ msgid "Duration" msgstr "الفترة الزمنية" #. module: purchase_custom_report +#: model:ir.model.fields,field_description:purchase_custom_report.field_employee_custody_report__employee_id #: model:ir.model.fields,field_description:purchase_custom_report.field_employee_purchase_report__employee_ids #: model:ir.model.fields,field_description:purchase_custom_report.field_employee_purchase_requistion__employee_ids #: model:ir.model.fields,field_description:purchase_custom_report.field_purchase_general_report__employee_ids @@ -313,6 +347,15 @@ msgstr "الفترة الزمنية" msgid "Employee" msgstr "الموظف" +#. module: purchase_custom_report +#: model:ir.actions.act_window,name:purchase_custom_report.wizard_employee_custody_report_action +#: model:ir.actions.report,name:purchase_custom_report.action_report_employee_custody +#: model:ir.model,name:purchase_custom_report.model_employee_custody_report +#: model:ir.ui.menu,name:purchase_custom_report.report_employee_custody_menu +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Employee Custody Report" +msgstr "تقرير عهد الموظفين" + #. module: purchase_custom_report #: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_purchase_report msgid "Employee Products Purchase Detailed Report" @@ -335,9 +378,15 @@ msgid "Employee Purchase Reports" msgstr "تقرير أوامر شراء الموظفين " #. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details #: model_terms:ir.ui.view,arch_db:purchase_custom_report.purchase_general_report msgid "Employee:" -msgstr "الموظف" +msgstr "الموظف:" + +#. module: purchase_custom_report +#: model:ir.model.fields,field_description:purchase_custom_report.field_employee_custody_report__date_to +msgid "End Date" +msgstr "تاريخ النهاية" #. module: purchase_custom_report #: model:ir.model,name:purchase_custom_report.model_employee_purchase_report @@ -378,6 +427,7 @@ msgid "Group By" msgstr "مجمع بواسطة" #. module: purchase_custom_report +#: model:ir.model.fields,field_description:purchase_custom_report.field_employee_custody_report__id #: model:ir.model.fields,field_description:purchase_custom_report.field_employee_purchase_report__id #: model:ir.model.fields,field_description:purchase_custom_report.field_employee_purchase_requistion__id #: model:ir.model.fields,field_description:purchase_custom_report.field_purchase_committee_report__id @@ -385,6 +435,7 @@ msgstr "مجمع بواسطة" #: model:ir.model.fields,field_description:purchase_custom_report.field_purchase_order_report_wizard__id #: model:ir.model.fields,field_description:purchase_custom_report.field_purchase_requisition_wizard__id #: model:ir.model.fields,field_description:purchase_custom_report.field_purchase_total_report__id +#: model:ir.model.fields,field_description:purchase_custom_report.field_report_purchase_custom_report_employee_custody_report_details__id #: model:ir.model.fields,field_description:purchase_custom_report.field_report_purchase_custom_report_employee_purchase_report2__id #: model:ir.model.fields,field_description:purchase_custom_report.field_report_purchase_custom_report_employee_purchase_report__id #: model:ir.model.fields,field_description:purchase_custom_report.field_report_purchase_custom_report_purchase_comittee_report__id @@ -544,9 +595,30 @@ msgstr "الفترة الزمنية" #: model_terms:ir.ui.view,arch_db:purchase_custom_report.wizard_purchase_order_report_form #: model_terms:ir.ui.view,arch_db:purchase_custom_report.wizard_purchase_requisition_form #: model_terms:ir.ui.view,arch_db:purchase_custom_report.wizard_vendor_report_form +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.wizard_employee_custody_report_form msgid "Print" msgstr "اطبع" +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Source Document" +msgstr "المستند المصدر" + +#. module: purchase_custom_report +#: model:ir.model.fields,field_description:purchase_custom_report.field_employee_custody_report__date_from +msgid "Start Date" +msgstr "تاريخ البداية" + +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Request Date" +msgstr "تاريخ الطلب" + +#. module: purchase_custom_report +#: model_terms:ir.ui.view,arch_db:purchase_custom_report.employee_custody_report_details +msgid "Return Date" +msgstr "تاريخ الارجاع" + #. module: purchase_custom_report #: model:ir.model.fields.selection,name:purchase_custom_report.selection__employee_purchase_requistion__state__waiting msgid "Procurement Department" diff --git a/odex25_purchase/purchase_custom_report/reports/employee_custody_report.xml b/odex25_purchase/purchase_custom_report/reports/employee_custody_report.xml new file mode 100644 index 000000000..4d3ae8eba --- /dev/null +++ b/odex25_purchase/purchase_custom_report/reports/employee_custody_report.xml @@ -0,0 +1,87 @@ + + + + + diff --git a/odex25_purchase/purchase_custom_report/reports/report_actions.xml b/odex25_purchase/purchase_custom_report/reports/report_actions.xml index 7b759fe9c..fc6de96bf 100644 --- a/odex25_purchase/purchase_custom_report/reports/report_actions.xml +++ b/odex25_purchase/purchase_custom_report/reports/report_actions.xml @@ -69,4 +69,12 @@ purchase_custom_report.purchase_comittee_report_details + + Employee Custody Report + employee.custody.report + qweb-pdf + purchase_custom_report.employee_custody_report_details + purchase_custom_report.employee_custody_report_details + + diff --git a/odex25_purchase/purchase_custom_report/security/ir.model.access.csv b/odex25_purchase/purchase_custom_report/security/ir.model.access.csv index cd554c0ac..153366da1 100644 --- a/odex25_purchase/purchase_custom_report/security/ir.model.access.csv +++ b/odex25_purchase/purchase_custom_report/security/ir.model.access.csv @@ -6,3 +6,4 @@ access_purchase_requisition_wizard,access_purchase_requisition_wizard,model_purc access_purchase_committee_report,access_purchase_committee_report,model_purchase_committee_report,,1,1,1,1 access_purchase_order_report_wizard,access_purchase_order_report_wizard,model_purchase_order_report_wizard,,1,1,1,1 access_vendor_report_wizard,access_vendor_report_wizard,model_vendor_report_wizard,,1,1,1,1 +access_employee_custody_report,access_employee_custody_report,model_employee_custody_report,,1,1,1,1 diff --git a/odex25_purchase/purchase_custom_report/wizard/__init__.py b/odex25_purchase/purchase_custom_report/wizard/__init__.py index 8653299c8..095eec3ea 100644 --- a/odex25_purchase/purchase_custom_report/wizard/__init__.py +++ b/odex25_purchase/purchase_custom_report/wizard/__init__.py @@ -3,3 +3,4 @@ from . import purchase_total_report_wizard from . import employee_purchase_report_wizard from . import purchase_wizard from . import purchase_committee_report +from . import employee_custody_report_wizard diff --git a/odex25_purchase/purchase_custom_report/wizard/employee_custody_report_wizard.py b/odex25_purchase/purchase_custom_report/wizard/employee_custody_report_wizard.py new file mode 100644 index 000000000..a4a70d4be --- /dev/null +++ b/odex25_purchase/purchase_custom_report/wizard/employee_custody_report_wizard.py @@ -0,0 +1,75 @@ +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError +from collections import defaultdict + + +class EmployeeCustodyReport(models.TransientModel): + _name = 'employee.custody.report' + _description = 'Employee Custody Report' + + date_from = fields.Date(string='Start Date', required=True) + date_to = fields.Date(string='End Date', required=True) + employee_id = fields.Many2one('hr.employee', string='Employee') + department_id = fields.Many2one('hr.department', string='Department') + + def action_print(self): + data = { + 'date_from': self.date_from, + 'date_to': self.date_to, + 'employee_id': self.employee_id.id if self.employee_id else False, + 'department_id': self.department_id.id if self.department_id else False, + } + return self.env.ref('purchase_custom_report.action_report_employee_custody').report_action( + [], + data=data, + ) + + +class ReportEmployeeCustody(models.AbstractModel): + _name = 'report.purchase_custom_report.employee_custody_report_details' + + @api.model + def _get_report_values(self, docids, data=None): + domain = [('date', '>=', data['date_from']), ('date', '<=', data['date_to']), + ('state', 'not in', ['cancel', 'refuse'])] + + employee_obj = self.env['hr.employee'] + department_obj = self.env['hr.department'] + + if data.get('employee_id'): + domain.append(('employee_id', '=', data['employee_id'])) + if data.get('department_id'): + domain.append(('department_id', '=', data['department_id'])) + + requests = self.env['purchase.request'].search(domain) + + grouped_data = defaultdict(lambda: {'requests': [], 'operations': []}) + + for request in requests: + operations = self.env['account.asset.operation'].search([ + ('purchase_request_id', '=', request.id), + ('type', '=', 'assignment'), ('state', '!=', 'cancel') + ]) + + if operations: + key = (request.employee_id.id, request.department_id.id) + grouped_data[key]['requests'].append(request) + grouped_data[key]['operations'].extend(operations) + result_data = [] + for (employee_id, department_id), records in grouped_data.items(): + result_data.append({ + 'employee': employee_obj.browse(employee_id), + 'department': department_obj.browse(department_id), + 'requests': records['requests'], + 'operations': records['operations'], + }) + + return { + 'result_data': result_data, + 'date_from': data['date_from'], + 'date_to': data['date_to'], + 'employee': employee_obj.browse(data['employee_id']) if data.get('employee_id') else None, + 'department': department_obj.browse(data['department_id']) if data.get('department_id') else None, + 'show_department_column': not data.get('department_id'), + 'show_employee_column': not data.get('employee_id'), + } diff --git a/odex25_purchase/purchase_custom_report/wizard/employee_custody_report_wizard.xml b/odex25_purchase/purchase_custom_report/wizard/employee_custody_report_wizard.xml new file mode 100644 index 000000000..e97ca55aa --- /dev/null +++ b/odex25_purchase/purchase_custom_report/wizard/employee_custody_report_wizard.xml @@ -0,0 +1,44 @@ + + + + + Employee Custody Report + employee.custody.report + form + +
+ + + + + + + + + + + + +
+
+
+
+
+ + + Employee Custody Report + employee.custody.report + form + new + + + +
+
\ No newline at end of file diff --git a/odex25_purchase/purchase_custom_stock/__manifest__.py b/odex25_purchase/purchase_custom_stock/__manifest__.py index 6140cc077..3be176995 100755 --- a/odex25_purchase/purchase_custom_stock/__manifest__.py +++ b/odex25_purchase/purchase_custom_stock/__manifest__.py @@ -14,10 +14,12 @@ 'views/stock_warehouse.xml', 'views/stock_picking_view.xml', 'views/report_deliveryslip.xml', + 'views/product_template.xml', + 'views/account_asset_operation.xml', 'wizards/picking_purchase_request.xml' ], - 'depends': ['stock', 'purchase_requisition', 'purchase_requisition_custom'], + 'depends': ['stock', 'purchase_requisition', 'purchase_requisition_custom','exp_asset_custody_link'], 'installable': True, 'application': True, } diff --git a/odex25_purchase/purchase_custom_stock/i18n/ar_001.po b/odex25_purchase/purchase_custom_stock/i18n/ar_001.po index 42d5ec6aa..02e3276c8 100644 --- a/odex25_purchase/purchase_custom_stock/i18n/ar_001.po +++ b/odex25_purchase/purchase_custom_stock/i18n/ar_001.po @@ -20,6 +20,52 @@ msgstr "" msgid "Approve Warehouse" msgstr "" +#. module: purchase_custom_stock +#: model:ir.model.fields,field_description:purchase_custom_stock.field_purchase_request__asset_assign_count +msgid "Asset Assignment" +msgstr "إسناد أصل" + +#. module: purchase_custom_stock +#: model:ir.model.fields,field_description:purchase_custom_stock.field_product_product__asset_ok +#: model:ir.model.fields,field_description:purchase_custom_stock.field_product_template__asset_ok +msgid "Asset Expensed" +msgstr "يمكن صرفة كعهدة" + +#. module: purchase_custom_stock +#: model_terms:ir.ui.view,arch_db:purchase_custom_stock.purchase_request_form_inherit +msgid "Asset Lines" +msgstr "بند الأصول" + +#. module: purchase_custom_stock +#: model:ir.model,name:purchase_custom_stock.model_account_asset_operation +msgid "Asset Operation" +msgstr "عمليات الأصول" + +#. module: purchase_custom_stock +#: model:ir.model.fields,field_description:purchase_custom_stock.field_purchase_request__asset_release_count +msgid "Asset Release" +msgstr "إرجاع عهدة" + +#. module: purchase_custom_stock +#: model:ir.model.fields,field_description:purchase_custom_stock.field_purchase_request__asset_request_line_ids +msgid "Asset Request Line" +msgstr "" + +#. module: purchase_custom_stock +#: model:ir.actions.act_window,name:purchase_custom_stock.action_account_asset_assignment +msgid "Assets Assignment" +msgstr "إسناد عهدة" + +#. module: purchase_custom_stock +#: model:ir.actions.act_window,name:purchase_custom_stock.action_account_asset_release +msgid "Assets Release" +msgstr "إرجاع عهدة" + +#. module: purchase_custom_stock +#: model_terms:ir.ui.view,arch_db:purchase_custom_stock.purchase_request_form_inherit +msgid "Assignments" +msgstr "الإسناد" + #. module: purchase_custom_stock #: model:ir.model.fields,field_description:purchase_custom_stock.field_purchase_request_picking_wizard__is_available msgid "Available" @@ -51,7 +97,7 @@ msgstr "إلغاء" #. module: purchase_custom_stock #: model_terms:ir.ui.view,arch_db:purchase_custom_stock.purchase_request_picking_wizard_view_form msgid "Cancel Request Reason" -msgstr "" +msgstr "سبب الرفض" #. module: purchase_custom_stock #: code:addons/purchase_custom_stock/models/purchase_request.py:0 @@ -318,3 +364,45 @@ msgstr "" #, python-format msgid "Expected Price MUST be at Least ONE!" msgstr "1 يجب أن يكون السعر التقديري على الأقل!" + +#. module: purchase_custom_stock +#: model:ir.model.fields,field_description:purchase_custom_stock.field_purchase_request__has_asset_product_line +msgid "Has Asset Product" +msgstr "يحتوي على منتج أصل" + +#. module: purchase_custom_stock +#: code:addons/purchase_custom_stock/models/purchase_request.py:0 +#: code:addons/purchase_custom_stock/models/purchase_request.py:0 +#, python-format +msgid "Please Select an asset" +msgstr "يرجى اختيار أصل" + +#. module: purchase_custom_stock +#: model:ir.model,name:purchase_custom_stock.model_product_template +msgid "Product Template" +msgstr "قالب المنتج" + +#. module: purchase_custom_stock +#: model_terms:ir.ui.view,arch_db:purchase_custom_stock.purchase_request_form_inherit +msgid "Releases" +msgstr "إرجاع عهدة" + +#. module: purchase_custom_stock +#: model_terms:ir.ui.view,arch_db:purchase_custom_stock.purchase_request_form_inherit +msgid "Return Done" +msgstr "تم ارجاع العهده" + +#. module: purchase_custom_stock +#: model:ir.model.fields,field_description:purchase_custom_stock.field_purchase_request__show_asset_release_button +msgid "Show Asset Release Button" +msgstr "عرض زر تحرير الأصل" + +#. module: purchase_custom_stock +#: model:ir.model.fields,field_description:purchase_custom_stock.field_account_asset_operation__purchase_request_id +msgid "Source Document" +msgstr "المستند المصدر" + +#. module: purchase_custom_stock +#: model:ir.model,name:purchase_custom_stock.model_asset_custody_line +msgid "asset.custody.line" +msgstr "بند أصل" \ No newline at end of file diff --git a/odex25_purchase/purchase_custom_stock/models/__init__.py b/odex25_purchase/purchase_custom_stock/models/__init__.py index c71ecd49b..466ba0e7a 100755 --- a/odex25_purchase/purchase_custom_stock/models/__init__.py +++ b/odex25_purchase/purchase_custom_stock/models/__init__.py @@ -2,3 +2,6 @@ from . import purchase_request from . import stock_warehouse from . import stock_picking +from . import product_template +from . import employee_custody +from . import account_asset_operation \ No newline at end of file diff --git a/odex25_purchase/purchase_custom_stock/models/account_asset_operation.py b/odex25_purchase/purchase_custom_stock/models/account_asset_operation.py new file mode 100644 index 000000000..088ca2075 --- /dev/null +++ b/odex25_purchase/purchase_custom_stock/models/account_asset_operation.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api, _, exceptions + + +class AccountAssetOperation(models.Model): + _inherit = 'account.asset.operation' + + purchase_request_id = fields.Many2one(comodel_name='purchase.request',string="Source Document") diff --git a/odex25_purchase/purchase_custom_stock/models/employee_custody.py b/odex25_purchase/purchase_custom_stock/models/employee_custody.py new file mode 100644 index 000000000..1dbaa3af9 --- /dev/null +++ b/odex25_purchase/purchase_custom_stock/models/employee_custody.py @@ -0,0 +1,13 @@ +#-*- coding: utf-8 -*- +from odoo import models, fields, api, _, exceptions +from odoo import SUPERUSER_ID + + +# from datetime import datetime , date + +class EmployeeCustodyLine(models.Model): + _inherit = 'asset.custody.line' + + purchase_request_id = fields.Many2one(comodel_name='purchase.request', string="Purchase Request") + + diff --git a/odex25_purchase/purchase_custom_stock/models/product_template.py b/odex25_purchase/purchase_custom_stock/models/product_template.py new file mode 100644 index 000000000..78b4a93c0 --- /dev/null +++ b/odex25_purchase/purchase_custom_stock/models/product_template.py @@ -0,0 +1,9 @@ +#-*- coding: utf-8 -*- +from odoo import models, fields , api, _ +from datetime import datetime +from datetime import timedelta + +class ProductTempInherit(models.Model): + _inherit = 'product.template' + + asset_ok = fields.Boolean(string="Asset Expensed") \ No newline at end of file diff --git a/odex25_purchase/purchase_custom_stock/models/purchase_request.py b/odex25_purchase/purchase_custom_stock/models/purchase_request.py index 9d253f0aa..b03930fa0 100644 --- a/odex25_purchase/purchase_custom_stock/models/purchase_request.py +++ b/odex25_purchase/purchase_custom_stock/models/purchase_request.py @@ -1,5 +1,5 @@ from odoo import api, fields, models, _ -from odoo.exceptions import UserError, ValidationError +from odoo.exceptions import UserError, ValidationError, Warning from datetime import datetime, timedelta, date from odoo.tools.misc import get_lang from odoo.addons.purchase.models.purchase import PurchaseOrder as Purchase @@ -8,47 +8,85 @@ from odoo.addons.purchase.models.purchase import PurchaseOrder as Purchase class PurchaseRequest(models.Model): _inherit = 'purchase.request' - - warehouse_id = fields.Many2one("stock.warehouse", string= "Warehouse" ,copy=False) - view_location_id = fields.Many2one(related="warehouse_id.view_location_id", string= "Warehouse",copy=False ) - location_id = fields.Many2one("stock.location", string= "Location",domain="[('id', 'child_of', view_location_id),('usage', '=', 'internal')]",copy=False ) - picking_id=fields.Many2one("stock.picking",copy=False) - edit_locations=fields.Boolean(string="Edit Locations",compute='compute_edit_locations',copy=False) + warehouse_id = fields.Many2one("stock.warehouse", string="Warehouse", copy=False) + view_location_id = fields.Many2one(related="warehouse_id.view_location_id", string="Warehouse", copy=False) + location_id = fields.Many2one("stock.location", string="Location", + domain="[('id', 'child_of', view_location_id),('usage', '=', 'internal')]", + copy=False) + picking_id = fields.Many2one("stock.picking", copy=False) + edit_locations = fields.Boolean(string="Edit Locations", compute='compute_edit_locations', copy=False) state = fields.Selection( - [('draft', 'Draft'), ('direct_manager', 'Direct Manager'),('warehouse', 'Warehouses Department'),('wait_for_send', 'Wait For Sent'), + [('draft', 'Draft'), ('direct_manager', 'Direct Manager'), ('warehouse', 'Warehouses Department'), + ('wait_for_send', 'Wait For Sent'), ('initial', 'Initial Engagement'), - ('waiting', 'In Purchase'),('employee', 'Employee Delivery'),('done', 'Done'), ('cancel', 'Cancel'), ('refuse', 'Refuse')], default="draft", - tracking=True,copy=False ) - show_emp_button=fields.Boolean(compute='show_employee_button',copy=False) - show_approve_warehouse=fields.Boolean("Approve Warehouse",compute='show_approve_warehouse_button') + ('waiting', 'In Purchase'), ('employee', 'Employee Delivery'), ('done', 'Done'), ('cancel', 'Cancel'), + ('refuse', 'Refuse')], default="draft", + tracking=True, copy=False) + show_emp_button = fields.Boolean(compute='show_employee_button', copy=False) + show_approve_warehouse = fields.Boolean("Approve Warehouse", compute='show_approve_warehouse_button') + has_asset_product_line = fields.Boolean(string="Has Asset Product", compute="_compute_has_asset_product_line", + store=True) + show_asset_release_button = fields.Boolean(string="Show Asset Release Button", + compute="_compute_show_asset_release_button") + asset_request_line_ids = fields.One2many('asset.custody.line', 'purchase_request_id') + asset_assign_count = fields.Integer(compute='_asset_assign_count', string='Asset Assignment') + asset_release_count = fields.Integer(compute='_asset_release_count', string='Asset Release') + + def _asset_assign_count(self): + self.asset_assign_count = len( + self.env['asset.custody.line'].search([('purchase_request_id', '=', self.id)])) + + def _asset_release_count(self): + self.asset_release_count = len( + self.env['asset.custody.line'].search([('purchase_request_id', '=', self.id)])) + + @api.depends('line_ids.product_id') + def _compute_has_asset_product_line(self): + for rec in self: + rec.has_asset_product_line = any( + rec.line_ids.filtered(lambda line: line.product_id.asset_ok) + ) + + def _compute_show_asset_release_button(self): + for rec in self: + operations = self.env['account.asset.operation'].search([ + ('purchase_request_id', '=', rec.id), + ('type', '=', 'assignment') + ]) + states = operations.mapped('state') + if operations and set(states).issubset({'done', 'cancel'}) and 'done' in states: + rec.show_asset_release_button = True + else: + rec.show_asset_release_button = False def show_employee_button(self): """show only for the create employee""" for rec in self: - rec.show_emp_button=False + rec.show_emp_button = False if rec.create_uid.id == self.env.user.id and rec.state == 'employee': - rec.show_emp_button=True + rec.show_emp_button = True - @api.depends("warehouse_id") + @api.depends("warehouse_id", "line_ids", "state") def show_approve_warehouse_button(self): """show only for the show aaprove warhouse employee""" for rec in self: - rec.show_approve_warehouse=False - if rec.warehouse_id.manager_id.user_id.id == self.env.user.id and rec.state == 'warehouse': - rec.show_approve_warehouse=True - + rec.show_approve_warehouse = False + if (rec.warehouse_id.manager_id.user_id.id == self.env.user.id or any( + rec.line_ids.filtered(lambda line: line.product_id.asset_ok))) and rec.state == 'warehouse': + rec.show_approve_warehouse = True def compute_edit_locations(self): """Compute For Group Edit Warehouse/Locations""" for rec in self: if self.env.user.has_group("stock.group_stock_user") or self.env.user.has_group( - "stock.group_stock_manager") : + "stock.group_stock_manager"): rec.edit_locations = True else: rec.edit_locations = False def action_confirm(self): - init_active = self.env['ir.module.module'].sudo().search([('name', '=', 'initial_engagement_budget'), ('state', '=', 'installed')], limit=1) + init_active = self.env['ir.module.module'].sudo().search( + [('name', '=', 'initial_engagement_budget'), ('state', '=', 'installed')], limit=1) init_budget = self.initial_engagement_activate if len(self.line_ids) == 0: raise ValidationError(_("Can't Confirm Request With No Item!")) @@ -56,17 +94,14 @@ class PurchaseRequest(models.Model): raise ValidationError(_("Please Select department for employee")) direct_manager = self.sudo().department_id.manager_id if direct_manager and direct_manager.user_id and self.env.user.id != direct_manager.user_id.id: - raise ValidationError(_("only %s Direct Manager can approve the order"%self.sudo().employee_id.name)) - if any(self.line_ids.filtered(lambda line: line.product_id.type == "product")): + raise ValidationError(_("only %s Direct Manager can approve the order" % self.sudo().employee_id.name)) + if any(self.line_ids.filtered(lambda line: line.product_id.type == "product" or line.product_id.asset_ok)): self.write({'state': 'warehouse'}) else: for rec in self.line_ids: - rec.write({"qty_purchased":rec.qty}) + rec.write({"qty_purchased": rec.qty}) self.write({'state': 'wait_for_send' if init_budget else 'waiting'}) - - - def create_requisition(self): """inherit for take in considiration available qty """ self.is_requisition = True @@ -92,10 +127,10 @@ class PurchaseRequest(models.Model): 'user_id': self.sudo().employee_id.user_id.id, 'line_ids': line_ids, 'res_id': self.id, - 'res_model':"purchase.request", + 'res_model': "purchase.request", }) - self.write({'purchase_create': True,'state':'employee'}) + self.write({'purchase_create': True, 'state': 'employee'}) return { 'name': "Request for Quotation", @@ -106,14 +141,14 @@ class PurchaseRequest(models.Model): } def create_purchase_order2(self): - if not self.partner_id : + if not self.partner_id: raise ValidationError(_("Please Insert a Vendor")) line_ids = [] for line in self.line_ids.filtered(lambda line: line.qty_purchased > 0): line_ids.append((0, 6, { 'product_id': line.product_id.id, 'product_qty': line.qty_purchased, - 'name':line.description or line.product_id.name, + 'name': line.description or line.product_id.name, 'department_name': self.sudo().employee_id.department_id.id, 'account_analytic_id': line.account_id.id, 'date_planned': datetime.today(), @@ -128,11 +163,11 @@ class PurchaseRequest(models.Model): 'purpose': self.purchase_purpose, 'purchase_cost': 'product_line', 'order_line': line_ids, - 'res_model':"purchase.request", + 'res_model': "purchase.request", 'res_id': self.id, # Reference to the current purchase order }) - self.write({'purchase_create': True,'state':'employee'}) + self.write({'purchase_create': True, 'state': 'employee'}) return { 'name': "Purchase orders from employee", @@ -142,19 +177,30 @@ class PurchaseRequest(models.Model): 'res_id': purchase_order.id} def action_confirm_picking(self): - if len(self.line_ids) == 0: + if not self.line_ids: raise ValidationError(_("Can't Confirm Request With No Item!")) if not self.department_id: raise ValidationError(_("Please Select department for employee")) - picking_id= self.env.ref('purchase_custom_stock.stock_picking_type_stock') + picking_id = self.env.ref('purchase_custom_stock.stock_picking_type_stock') - available=False - if any(self.line_ids.filtered(lambda line: line.product_id.type == 'product' )): - storable_product_lines=self.line_ids.filtered(lambda line: line.product_id.type == 'product' ) - non_storable_product = self.line_ids - storable_product_lines - if any(storable_product_lines.filtered(lambda line: line.available_qty > 0)): + available = False + if any(self.line_ids.filtered(lambda line: line.product_id.type in ['product', 'consu'])): + storable_product_lines = self.line_ids.filtered(lambda line: line.product_id.type == 'product') + asset_product_lines = self.line_ids.filtered(lambda line: line.product_id.asset_ok) + consu_product_not_asset_lines = self.line_ids.filtered( + lambda line: line.product_id.type == 'consu' and not line.product_id.asset_ok) + non_storable_product = self.line_ids - storable_product_lines - asset_product_lines - consu_product_not_asset_lines + if any(storable_product_lines.filtered(lambda line: line.available_qty > 0)) or any( + consu_product_not_asset_lines.filtered(lambda line: line.available_qty > 0)): available = True - if any(storable_product_lines.filtered(lambda store_line: store_line.qty > store_line.available_qty)): + if any(storable_product_lines.filtered( + lambda store_line: store_line.qty > store_line.available_qty)) or any( + asset_product_lines.filtered(lambda asset_line: asset_line.qty > asset_line.available_qty)) or any( + consu_product_not_asset_lines.filtered(lambda asset_line: asset_line.qty > asset_line.available_qty)): + if self.has_asset_product_line: + if not self.asset_request_line_ids: + raise Warning(_('Please Select an asset')) + self.create_asset_operation() context = {} view = self.env.ref('purchase_custom_stock.purchase_request_picking_wizard_view_form') wiz = self.env['purchase.request_picking.wizard'] @@ -176,25 +222,31 @@ class PurchaseRequest(models.Model): 'context': context, } else: - picking_vals = { - "picking_type_id": self.env.ref('purchase_custom_stock.stock_picking_type_stock').id, - "origin": self.name, - "location_id": self.location_id.id, - "location_dest_id": picking_id.default_location_dest_id.id - } - move_vals = [] - for line in storable_product_lines: - move_vals.append((0, 0, { - "product_id": line.product_id.id, - "name": line.product_id.name, - "product_uom": line.product_id.uom_id.id, - 'product_uom_qty': line.qty, + product_lines = storable_product_lines + consu_product_not_asset_lines + if product_lines: + move_vals = [] + for line in product_lines: + move_vals.append((0, 0, { + "product_id": line.product_id.id, + "name": line.product_id.name, + "product_uom": line.product_id.uom_id.id, + 'product_uom_qty': line.qty, - })) - line.qty_purchased = 0 - picking_vals.update({'move_lines': move_vals}) - picking_id = self.env['stock.picking'].create(picking_vals) - self.picking_id = picking_id.id + })) + line.qty_purchased = 0 + picking_vals = { + "picking_type_id": self.env.ref('purchase_custom_stock.stock_picking_type_stock').id, + "origin": self.name, + "location_id": self.location_id.id, + "location_dest_id": picking_id.default_location_dest_id.id, + 'move_lines': move_vals, + } + picking_id = self.env['stock.picking'].create(picking_vals) + self.picking_id = picking_id.id + if self.has_asset_product_line: + if not self.asset_request_line_ids: + raise Warning(_('Please Select an asset')) + self.create_asset_operation() if non_storable_product: for rec in non_storable_product: rec.qty_purchased = rec.qty @@ -206,9 +258,6 @@ class PurchaseRequest(models.Model): line.qty_purchased = line.qty self.write({'state': 'waiting'}) - - - def open_picking(self): return { @@ -216,99 +265,143 @@ class PurchaseRequest(models.Model): 'view_mode': 'form', 'view_type': 'form', 'res_model': 'stock.picking', - 'res_id':self.picking_id.id, + 'res_id': self.picking_id.id, 'type': 'ir.actions.act_window', - 'target':'current', + 'target': 'current', } def action_available_qty(self): for rec in self: - if not rec.location_id: - raise ValidationError(_("Please Insert Location first")) for line in rec.line_ids: - line.available_qty = self.env['stock.quant'].search( - [('product_id', '=', line.product_id.id), ('location_id', '=', rec.location_id.id)], - limit=1).available_quantity - line.qty_purchased=line.qty-line.available_qty - def write(self,vals): + if line.product_id.asset_ok: + asset_count = self.env['account.asset'].search_count([ + ('product_id', '=', line.product_id.id), + ('status', 'in', ['new', 'available']) + ]) + line.available_qty = asset_count + line.qty_purchased = line.qty - asset_count + else: + if not rec.location_id: + raise ValidationError(_("Please Insert Location first")) + line.available_qty = self.env['stock.quant'].search( + [('product_id', '=', line.product_id.id), ('location_id', '=', rec.location_id.id)], + limit=1).available_quantity + line.qty_purchased = line.qty - line.available_qty + + def write(self, vals): """Ovveride Send Notification On state""" - res=super(PurchaseRequest,self).write(vals) - if 'state' in vals : + res = super(PurchaseRequest, self).write(vals) + if 'state' in vals: if vals['state'] == 'direct_manager': - direct_manager = self.sudo().department_id.manager_id - if direct_manager and direct_manager.user_id: - if self.env.user.partner_id.lang == 'ar_001': - body = 'عزيزى %s موافقتك مطلوبة على %s ' % (direct_manager.name, self.name) - else: - body = 'Dear %s your approval is required on %s ' % (direct_manager.name, self.name) - self.message_notify(body=body, - partner_ids=[direct_manager.user_id.partner_id.id]) + direct_manager = self.sudo().department_id.manager_id + if direct_manager and direct_manager.user_id: + if self.env.user.partner_id.lang == 'ar_001': + body = 'عزيزى %s موافقتك مطلوبة على %s ' % (direct_manager.name, self.name) + else: + body = 'Dear %s your approval is required on %s ' % (direct_manager.name, self.name) + self.message_notify(body=body, + partner_ids=[direct_manager.user_id.partner_id.id]) elif vals['state'] == 'warehouse': - # stock_group = self.env.ref('stock.group_stock_manager') - warehouse=self.env['stock.warehouse'].sudo().search([('department_id', '=', self.department_id.id)]) - stock_employee=False - if warehouse and warehouse.manager_id: - stock_employee = warehouse.manager_id + # stock_group = self.env.ref('stock.group_stock_manager') + warehouse = self.env['stock.warehouse'].sudo().search([('department_id', '=', self.department_id.id)]) + stock_employee = False + if warehouse and warehouse.manager_id: + stock_employee = warehouse.manager_id - if stock_employee and stock_employee.user_id.partner_id.id: - if self.env.user.partner_id.lang=='ar_001': - body = 'عزيزى %s موافقتك مطلوبة على %s ' % (stock_employee.name, self.name) - else: - body='Dear %s your approval is required on %s ' % (stock_employee.name, self.name) - self.message_notify(body=body, - partner_ids=[stock_employee.user_id.partner_id.id]) - # self.message_post(body=body, - # message_type='notification', - # author_id=self.env.user.partner_id.id, sticky=True, - # subtype_id=self.env.ref("mail.mt_comment").id, - # partner_ids=[stock_employee.user_id.partner_id.id]) + if stock_employee and stock_employee.user_id.partner_id.id: + if self.env.user.partner_id.lang == 'ar_001': + body = 'عزيزى %s موافقتك مطلوبة على %s ' % (stock_employee.name, self.name) + else: + body = 'Dear %s your approval is required on %s ' % (stock_employee.name, self.name) + self.message_notify(body=body, + partner_ids=[stock_employee.user_id.partner_id.id]) + # self.message_post(body=body, + # message_type='notification', + # author_id=self.env.user.partner_id.id, sticky=True, + # subtype_id=self.env.ref("mail.mt_comment").id, + # partner_ids=[stock_employee.user_id.partner_id.id]) elif vals['state'] == 'waiting': - purchase_group = self.env.ref('purchase.group_purchase_manager') - purchase_users = self.env['res.users'].search([('groups_id', '=', purchase_group.id)]) - for user in purchase_users: - purchase_employee = self.env['hr.employee'].search([('user_id', '=', user.id)], limit=1) - if self.env.user.partner_id.lang == 'ar_001': - body = 'عزيزى %s موافقتك مطلوبة على %s ' % (purchase_employee.name, self.name) - else: - body = 'Dear %s your approval is required on %s ' % (purchase_employee.name, self.name) - if purchase_employee and user.partner_id.id: - self.message_notify(body=body, - partner_ids=[user.partner_id.id]) - # self.message_post(body=body, - # message_type='notification', - # author_id=self.env.user.partner_id.id, sticky=True, - # subtype_id=self.env.ref("mail.mt_comment").id, - # partner_ids=[user.partner_id.id]) + purchase_group = self.env.ref('purchase.group_purchase_manager') + purchase_users = self.env['res.users'].search([('groups_id', '=', purchase_group.id)]) + for user in purchase_users: + purchase_employee = self.env['hr.employee'].search([('user_id', '=', user.id)], limit=1) + if self.env.user.partner_id.lang == 'ar_001': + body = 'عزيزى %s موافقتك مطلوبة على %s ' % (purchase_employee.name, self.name) + else: + body = 'Dear %s your approval is required on %s ' % (purchase_employee.name, self.name) + if purchase_employee and user.partner_id.id: + self.message_notify(body=body, + partner_ids=[user.partner_id.id]) + # self.message_post(body=body, + # message_type='notification', + # author_id=self.env.user.partner_id.id, sticky=True, + # subtype_id=self.env.ref("mail.mt_comment").id, + # partner_ids=[user.partner_id.id]) elif vals['state'] == 'employee': - if self.sudo().employee_id and self.sudo().employee_id.user_id: - if self.env.user.partner_id.lang == 'ar_001': - body = 'عزيزى %s يرجى تاكيد استلامك على %s ' % (self.sudo().employee_id.name, self.name) - else: - body = 'Dear %s please confirm Your receipt on %s ' % (self.sudo().employee_id.name, self.name) + if self.sudo().employee_id and self.sudo().employee_id.user_id: + if self.env.user.partner_id.lang == 'ar_001': + body = 'عزيزى %s يرجى تاكيد استلامك على %s ' % (self.sudo().employee_id.name, self.name) + else: + body = 'Dear %s please confirm Your receipt on %s ' % (self.sudo().employee_id.name, self.name) - self.message_notify(body=body, - partner_ids=[self.sudo().employee_id.user_id.partner_id.id]) - # self.message_post( - # body=body, - # message_type='notification', - # author_id=self.env.user.partner_id.id, sticky=True, - # subtype_id=self.env.ref("mail.mt_comment").id, - # partner_ids=[self.sudo().employee_id.user_id.partner_id.id]) + self.message_notify(body=body, + partner_ids=[self.sudo().employee_id.user_id.partner_id.id]) + # self.message_post( + # body=body, + # message_type='notification', + # author_id=self.env.user.partner_id.id, sticky=True, + # subtype_id=self.env.ref("mail.mt_comment").id, + # partner_ids=[self.sudo().employee_id.user_id.partner_id.id]) return res + def create_asset_operation(self): + for asset in self.asset_request_line_ids: + data = { + 'date': self.date, + 'asset_id': asset.asset_id.id, + 'type': 'assignment', + 'custody_type': asset.custody_type, + 'custody_period': asset.custody_period, + 'state': 'draft', + 'user_id': self.env.uid, + 'new_employee_id': self.employee_id.id, + 'new_department_id': self.department_id.id, + 'purchase_request_id': self.id, + } + self.env['account.asset.operation'].create(data) + def asset_operation_release(self): + for asset in self.asset_request_line_ids: + data = { + 'name': asset.asset_id.name, + 'date': self.date, + 'asset_id': asset.asset_id.id, + 'type': 'release', + 'custody_type': asset.custody_type, + 'custody_period': asset.custody_period, + 'state': 'draft', + 'user_id': self.env.uid, + 'current_employee_id': self.employee_id.id, + 'new_employee_id': self.employee_id.id, + 'current_department_id': self.department_id.id, + 'purchase_request_id': self.id, + + } + self.env['account.asset.operation'].create(data) + + def return_asset(self): + self.asset_operation_release() class PurchaseRequestLine(models.Model): _inherit = 'purchase.request.line' _description = 'purchase request line' - qty_purchased = fields.Integer(string='Purchase Qty',copy=False) + qty_purchased = fields.Integer(string='Purchase Qty', copy=False) qty = fields.Integer(string='Demand Qty') - available_qty = fields.Integer(string='Available Qty',copy=False) - + available_qty = fields.Integer(string='Available Qty', copy=False) @api.constrains('qty') def qty_validation(self): @@ -321,5 +414,3 @@ class PurchaseRequestLine(models.Model): for rec in self: if rec.request_id.initial_engagement_activate == True and rec.expected_price <= 0: raise ValidationError(_("Expected Price MUST be at Least ONE!")) - - diff --git a/odex25_purchase/purchase_custom_stock/views/account_asset_operation.xml b/odex25_purchase/purchase_custom_stock/views/account_asset_operation.xml new file mode 100644 index 000000000..e3c4e1b01 --- /dev/null +++ b/odex25_purchase/purchase_custom_stock/views/account_asset_operation.xml @@ -0,0 +1,13 @@ + + + + account.asset.operation.form + account.asset.operation + + + + + + + + \ No newline at end of file diff --git a/odex25_purchase/purchase_custom_stock/views/product_template.xml b/odex25_purchase/purchase_custom_stock/views/product_template.xml new file mode 100644 index 000000000..f764a4bfb --- /dev/null +++ b/odex25_purchase/purchase_custom_stock/views/product_template.xml @@ -0,0 +1,16 @@ + + + + product.template.form + product.template + + +
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/odex25_purchase/purchase_custom_stock/views/purchase_request.xml b/odex25_purchase/purchase_custom_stock/views/purchase_request.xml index 500a494dc..edad9c9aa 100644 --- a/odex25_purchase/purchase_custom_stock/views/purchase_request.xml +++ b/odex25_purchase/purchase_custom_stock/views/purchase_request.xml @@ -2,17 +2,56 @@ + + Assets Assignment + account.asset.operation + tree,form + { 'create': False } + [('purchase_request_id', '=', active_id),('type', '=', 'assignment')] + + + + + Assets Release + account.asset.operation + tree,form + { 'create': False } + [('purchase_request_id', '=', active_id),('type', '=', 'release')] + + + + purchase.request.view.form purchase.request + + + +