From 00a4d9e6d15b408f785d6adfa4d96927b392d80e Mon Sep 17 00:00:00 2001 From: raman Date: Wed, 2 Jul 2025 12:22:31 +0530 Subject: [PATCH 01/49] payrolland bio metric bug --- .../consolidated_batch_payslip/__init__.py | 1 + .../__manifest__.py | 21 + .../models/__init__.py | 1 + .../models/batch_payslip.py | 299 +++++++++++ .../security/ir.model.access.csv | 1 + .../pqgrid_batch_payslip.js | 508 ++++++++++++++++++ .../pqgrid_batch_payslip.xml | 6 + .../views/batch_payslip_view.xml | 18 + .../models/biometric_device_details.py | 2 +- 9 files changed, 856 insertions(+), 1 deletion(-) create mode 100644 addons_extensions/consolidated_batch_payslip/__init__.py create mode 100644 addons_extensions/consolidated_batch_payslip/__manifest__.py create mode 100644 addons_extensions/consolidated_batch_payslip/models/__init__.py create mode 100644 addons_extensions/consolidated_batch_payslip/models/batch_payslip.py create mode 100644 addons_extensions/consolidated_batch_payslip/security/ir.model.access.csv create mode 100644 addons_extensions/consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.js create mode 100644 addons_extensions/consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.xml create mode 100644 addons_extensions/consolidated_batch_payslip/views/batch_payslip_view.xml diff --git a/addons_extensions/consolidated_batch_payslip/__init__.py b/addons_extensions/consolidated_batch_payslip/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/addons_extensions/consolidated_batch_payslip/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/addons_extensions/consolidated_batch_payslip/__manifest__.py b/addons_extensions/consolidated_batch_payslip/__manifest__.py new file mode 100644 index 000000000..eff682273 --- /dev/null +++ b/addons_extensions/consolidated_batch_payslip/__manifest__.py @@ -0,0 +1,21 @@ +{ + 'name': 'Consolidated Payslip Grid (OWL)', + 'version': '1.0', + 'category': 'Human Resources', + 'summary': 'Editable Consolidated Payslip Grid (OWL + pqGrid)', + 'author': 'Raman Marikanti', + 'depends': ['hr_payroll', 'web'], + 'data': [ + 'security/ir.model.access.csv', + 'views/batch_payslip_view.xml', + ], + 'assets': { + 'web.assets_backend': [ + # Internal module JS and XML files (ensure correct paths within 'static/src') + 'consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.js', + 'consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.xml', + ], +}, + + 'installable': True, +} diff --git a/addons_extensions/consolidated_batch_payslip/models/__init__.py b/addons_extensions/consolidated_batch_payslip/models/__init__.py new file mode 100644 index 000000000..86c0c139f --- /dev/null +++ b/addons_extensions/consolidated_batch_payslip/models/__init__.py @@ -0,0 +1 @@ +from . import batch_payslip \ No newline at end of file diff --git a/addons_extensions/consolidated_batch_payslip/models/batch_payslip.py b/addons_extensions/consolidated_batch_payslip/models/batch_payslip.py new file mode 100644 index 000000000..762af2245 --- /dev/null +++ b/addons_extensions/consolidated_batch_payslip/models/batch_payslip.py @@ -0,0 +1,299 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import UserError, ValidationError +import datetime + +class HrPayslipRun(models.Model): + _inherit = 'hr.payslip.run' + + @api.model + def get_consolidated_attendance_data(self, payslip_run_id): + """ + Returns consolidated attendance and leave data for all employees in the payslip run + """ + # Get all payslips in this batch + payslips = self.env['hr.payslip'].search([('payslip_run_id', '=', payslip_run_id)]) + + if not payslips: + return [] + + result = [] + for slip in payslips: + employee = slip.employee_id + contract = slip.contract_id + + # Get attendance data + attendance_days = self._get_attendance_days(slip) + worked_days = self._get_worked_days(slip) + leave_days = self._get_leave_days(slip) + lop_days = self._get_lop_days(slip) + + # Get leave balances + leave_balances = self._get_leave_balances(employee,slip.date_from,slip.date_to) + leave_taken = self._get_leave_taken(slip) + + result.append({ + 'id': slip.id, + 'employee_id': (employee.id, employee.name), + 'employee_code': employee.employee_id or '', + 'department_id': (employee.department_id.id, + employee.department_id.name) if employee.department_id else False, + 'total_days': (slip.date_to - slip.date_from).days + 1, + 'worked_days': worked_days, + 'attendance_days': attendance_days, + 'leave_days': leave_days, + 'lop_days': lop_days, + 'doj':employee.doj, + 'birthday':employee.birthday, + 'bank': employee.bank_account_id.display_name if employee.bank_account_id else '-', + 'sick_leave_balance': leave_balances.get('LEAVE110', 0), + 'casual_leave_balance': leave_balances.get('LEAVE120', 0), + 'privilege_leave_balance': leave_balances.get('LEAVE100', 0), + 'sick_leave_taken': leave_taken.get('sick', 0), + 'casual_leave_taken': leave_taken.get('casual', 0), + 'privilege_leave_taken': leave_taken.get('privilege', 0), + 'state': slip.state, + 'lines':self.get_payslip_lines_data(slip), + }) + + return result + + @api.model + def sub_columns(self,payslip_run_id): + payslips = self.env['hr.payslip'].search([('payslip_run_id', '=', payslip_run_id)]) + + names = payslips.line_ids.filtered(lambda x:x.amount != 0) + code_name_dict = {line.code+line.name.replace(" ", "_")if line.code in ['REIMBURSEMENT','DEDUCTION'] else line.code : line.name for line in names} + + columns = [] + for code, name in code_name_dict.items(): + columns.append({ + 'title': name, + 'dataIndx': code, + 'width': 150, + 'editable': False, + 'summary': {'type': "sum_"}, + }) + return columns + + def save_consolidated_attendance_data(self, payslip_run_id, data): + """ + Saves the edited attendance and leave data from the grid + """ + self.ensure_one() + + for item in data: + slip = self.env['hr.payslip'].browse(item['id']) + if slip.state != 'draft': + raise UserError(_("Cannot edit payslip in %s state") % slip.state) + + # Update LOP days + if 'lop_days' in item: + self._update_lop_days(slip, float(item['lop_days'])) + + # Update leave days taken + leave_updates = {} + if 'sick_leave_taken' in item: + leave_updates['sick'] = float(item['sick_leave_taken']) + if 'casual_leave_taken' in item: + leave_updates['casual'] = float(item['casual_leave_taken']) + if 'privilege_leave_taken' in item: + leave_updates['privilege'] = float(item['privilege_leave_taken']) + + if leave_updates: + self._update_leave_taken(slip, leave_updates) + + return True + + def recalculate_lop_days(self, payslip_run_id): + """ + Recalculates LOP days for all payslips in the batch based on attendance + """ + self.ensure_one() + payslips = self.env['hr.payslip'].search([ + ('payslip_run_id', '=', payslip_run_id), + ('state', '=', 'draft') + ]) + + for slip in payslips: + attendance_days = self._get_attendance_days(slip) + expected_days = (slip.date_to - slip.date_from).days + 1 + lop_days = expected_days - attendance_days + self._update_lop_days(slip, lop_days) + + return True + + def validate_all_attendance_data(self, payslip_run_id): + """ + Marks all payslips in the batch as validated + """ + self.ensure_one() + payslips = self.env['hr.payslip'].search([ + ('payslip_run_id', '=', payslip_run_id), + ('state', '=', 'draft') + ]) + + if not payslips: + raise UserError(_("No draft payslips found in this batch")) + + payslips.write({'state': 'verify'}) + return True + + # Helper methods + def _get_attendance_days(self, payslip): + """ + Returns number of days employee was present (based on attendance records) + """ + attendance_records = self.env['hr.attendance'].search([ + ('employee_id', '=', payslip.employee_id.id), + ('check_in', '>=', payslip.date_from), + ('check_in', '<=', payslip.date_to) + ]) + + # Group by day + unique_days = set() + for att in attendance_records: + unique_days.add(att.check_in.date()) + + return len(unique_days) + + def _get_worked_days(self, payslip): + """ + Returns number of working days (excluding weekends and holidays) + """ + return payslip._get_worked_days_line_number_of_days('WORK100') # Assuming WORK100 is your work code + + def get_payslip_lines_data(self, payslip_id): + list = [] + for line in payslip_id.line_ids: + list.append({ + 'name': line.name, + 'code': line.code + line.name.replace(" ", "_")if line.code in ['REIMBURSEMENT','DEDUCTION'] else line.code, + 'category_id': line.category_id.name if line.category_id else False, + 'amount': line.amount, + 'quantity': line.quantity, + 'rate': line.rate + }) + return list + + def _get_leave_days(self, payslip): + """ + Returns total leave days taken in this period + """ + leave_lines = payslip.worked_days_line_ids.filtered( + lambda l: l.code in ['LEAVE110', 'LEAVE90', 'LEAVE100', 'LEAVE120'] # Your leave codes + ) + return sum(leave_lines.mapped('number_of_days')) + + def _get_lop_days(self, payslip): + """ + Returns LOP days from payslip + """ + lop_line = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'LEAVE90') + return lop_line.number_of_days if lop_line else 0 + + + def _get_leave_taken(self, payslip): + """ + Returns leave days taken in this payslip period + """ + leave_lines = payslip.worked_days_line_ids + return { + 'sick': sum(leave_lines.filtered(lambda l: l.code == 'LEAVE110').mapped('number_of_days')), + 'casual': sum(leave_lines.filtered(lambda l: l.code == 'LEAVE120').mapped('number_of_days')), + 'privilege': sum(leave_lines.filtered(lambda l: l.code == 'LEAVE100').mapped('number_of_days')), + } + + def _update_lop_days(self, payslip, days): + """ + Updates LOP days in the payslip + """ + WorkedDays = self.env['hr.payslip.worked_days'] + lop_line = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'LOP') + + if lop_line: + if days > 0: + lop_line.write({'number_of_days': days}) + else: + lop_line.unlink() + elif days > 0: + WorkedDays.create({ + 'payslip_id': payslip.id, + 'name': _('Loss of Pay'), + 'code': 'LOP', + 'number_of_days': days, + 'number_of_hours': days * 8, # Assuming 8-hour work day + 'contract_id': payslip.contract_id.id, + }) + + def _get_leave_balances(self, employee, date_from, date_to): + Leave = self.env['hr.leave'] + Allocation = self.env['hr.leave.allocation'] + leave_types = self.env['hr.leave.type'].search([]) + + balances = {} + for leave_type in leave_types: + # Approved allocations within or before payslip period + allocations = Allocation.search([ + ('employee_id', '=', employee.id), + ('state', '=', 'validate'), + ('holiday_status_id', '=', leave_type.id), + ('date_from', '<=', str(date_to)), # Allocation should be active during payslip + ]) + allocated = sum(alloc.number_of_days for alloc in allocations) + + # Approved leaves within the payslip period + leaves = Leave.search([ + ('employee_id', '=', employee.id), + ('state', '=', 'validate'), + ('holiday_status_id', '=', leave_type.id), + ('request_date_to', '<=', str(date_to)) + ]) + used = sum(leave.number_of_days for leave in leaves) + + # Key: leave code or fallback to name + code = leave_type.work_entry_type_id.code + balances[code] = allocated - used + + return balances + + def _update_leave_taken(self, payslip, leave_data): + """ + Updates leave days taken in the payslip + """ + WorkedDays = self.env['hr.payslip.worked_days'] + + for leave_type, days in leave_data.items(): + code = leave_type.upper() + line = payslip.worked_days_line_ids.filtered(lambda l: l.code == code) + + + if line: + if days > 0: + line.write({'number_of_days': days}) + else: + line.unlink() + elif days > 0: + WorkedDays.create({ + 'payslip_id': payslip.id, + 'name': _(leave_type.capitalize() + ' Leave'), + 'code': code, + 'number_of_days': days, + 'number_of_hours': days * 8, # Assuming 8-hour work day + 'contract_id': payslip.contract_id.id, + }) + + + +class HrPayslip(models.Model): + _inherit = 'hr.payslip' + + def get_payslip_lines_data(self, payslip_id): + payslip = self.browse(payslip_id) + return [{ + 'name': line.name, + 'code': line.code, + 'category_id': (line.category_id.id, line.category_id.name), + 'amount': line.amount, + 'quantity': line.quantity, + 'rate': line.rate + } for line in payslip.line_ids] \ No newline at end of file diff --git a/addons_extensions/consolidated_batch_payslip/security/ir.model.access.csv b/addons_extensions/consolidated_batch_payslip/security/ir.model.access.csv new file mode 100644 index 000000000..206887958 --- /dev/null +++ b/addons_extensions/consolidated_batch_payslip/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons_extensions/consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.js b/addons_extensions/consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.js new file mode 100644 index 000000000..963eb098d --- /dev/null +++ b/addons_extensions/consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.js @@ -0,0 +1,508 @@ +/** @odoo-module **/ +import { standardWidgetProps } from "@web/views/widgets/standard_widget_props"; +import { Component, onMounted, useRef, useState, onWillStart } from "@odoo/owl"; +import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; +import { loadJS, loadCSS } from "@web/core/assets"; + +export class ConsolidatedPayslipGrid extends Component { + static props = { + ...standardWidgetProps, + }; + + static template = "ConsolidatedPayslipGrid"; + + setup() { + this.orm = useService("orm"); + this.gridRef = useRef("gridContainer"); + this.state = useState({ + rows: [], + payslipRunId: this.props.record.resId || this.props.record.evalContext.id || false + }); + + onWillStart(async () => { + await this.loadDependencies(); + await this.loadGrid(); + }); + + onMounted(() => { + if (this.gridRef.el) { + this.renderGrid(); + } else { + console.error("Grid element not found"); + } + }); + } + + async loadDependencies() { + try { + await Promise.all([ + loadJS("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"), + loadJS("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js"), + loadCSS("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/themes/base/jquery-ui.min.css"), + loadJS("https://cdnjs.cloudflare.com/ajax/libs/pqGrid/3.5.1/pqgrid.min.js"), + loadJS("https://cdnjs.cloudflare.com/ajax/libs/jszip/2.6.1/jszip.min.js"), + loadJS("https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"), + loadCSS("https://cdnjs.cloudflare.com/ajax/libs/pqGrid/3.5.1/pqgrid.min.css"), + loadCSS("https://cdnjs.cloudflare.com/ajax/libs/pqGrid/3.5.1/themes/Office/pqgrid.min.css") + ]); + + // Set global jQuery references + window.$ = window.jQuery = window.$ || window.jQuery; + + // Verify pqGrid is loaded + if (!window.pq) { + throw new Error("pqGrid failed to load"); + } + } catch (error) { + console.error("Failed to load dependencies:", error); + this.showNotification("Failed to load required components", "danger"); + throw error; // Re-throw to prevent further execution + } + } + + async loadGrid() { + try { + if (!this.state.payslipRunId) { + console.error("No payslip run ID found"); + return; + } + + const records = await this.orm.call("hr.payslip.run", "get_consolidated_attendance_data", [this.state.payslipRunId]); + + + const data = records.map(rec => { + // Base object + const row = { + id: rec.id, + employee_id: rec.employee_id[0], + employee: rec.employee_id[1], + employee_code: rec.employee_code || "N/A", + department: rec.department_id ? rec.department_id[1] : "N/A", + total_days: rec.total_days || 0, + worked_days: rec.worked_days || 0, + attendance_days: rec.attendance_days || 0, + leave_days: rec.leave_days || 0, + lop_days: rec.lop_days || 0, + sick_leave_balance: rec.sick_leave_balance || 0, + casual_leave_balance: rec.casual_leave_balance || 0, + privilege_leave_balance: rec.privilege_leave_balance || 0, + sick_leave_taken: rec.sick_leave_taken || 0, + casual_leave_taken: rec.casual_leave_taken || 0, + privilege_leave_taken: rec.privilege_leave_taken || 0, + state: rec.state || 'draft', + doj: rec.doj, + bank: rec.bank, + birthday: rec.birthday, + lines: rec.lines || [] + }; + + // Add each line.code: line.amount into the row + for (const line of rec.lines || []) { + if (line.code) { + row[line.code] = line.amount; + } + } + + return row; + }); + + + this.state.rows = data; + this.renderGrid(); + } catch (error) { + console.error("Error loading data:", error); + } + } + + async renderGrid() { + if (!this.gridRef.el) return; + const columns = await this.getColumns(); + const agg = pq.aggregate; + + // Define custom aggregate functions + agg.sum_ = function(arr, col) { + return " " + agg.sum(arr, col).toFixed(2).toString(); + }; + + const groupModel = { + on: true, + //dataIndx: ["employee_code",""], + collapsed: [false, true], + merge: true, + showSummary: [true, true], + grandSummary: true, + render: () => "" // hides the group title row text + }; + + + const gridOptions = { + + selectionModel: { type: 'row' }, + width: "100%", + height: "100%", + groupModel: groupModel, + editable: true, + stripeRows:false, + editModel: { saveKey: $.ui.keyCode.ENTER }, + filterModel: { + on: true, + mode: "AND", + header: true, + autoSearch: true, + type: 'local', + minLength: 1 + }, + dataModel: { + data: this.state.rows, + location: "local", + sorting: "local", + paging: "local" + }, + menuIcon: true, + menuUI:{ + tabs: ['hideCols'] + }, + colModel: columns, + postRenderInterval: -1, + toolbar: { + items: [ + { + type: 'button', + label: 'Refresh', + icon: 'ui-icon-refresh', + listener: () => this.loadGrid() + }, + { + type: 'button', + label: 'Save Changes', + icon: 'ui-icon-disk', + listener: () => this.saveChanges() + }, + { + type: 'button', + label: 'Recalculate LOP', + icon: 'ui-icon-calculator', + listener: () => this.recalculateLOP() + }, + { + type: 'select', + label: 'Format: ', + attr: 'id="export_format"', + options: [{ xlsx: 'Excel', csv: 'Csv', htm: 'Html', json: 'Json'}] + }, + { + type: 'button', + label: "Export", + icon: 'ui-icon-arrowthickstop-1-s', + listener: function () { + + var format = $("#export_format").val(), + blob = this.exportData({ + //url: "/pro/demos/exportData", + format: format, + render: true + }); + if(typeof blob === "string"){ + blob = new Blob([blob]); + } + saveAs(blob, "PaySheet."+ format ); + } + }, + ] + }, + }; + + // Apply CSS and initialize grid + $(this.gridRef.el) + .css({ height: '600px', width: '100%' }) + .pqGrid(gridOptions) + .pqGrid("refreshDataAndView"); + } + + async refreshDataAndView() { + try { + await this.loadGrid(); + $(this.gridRef.el).pqGrid("refreshDataAndView"); + } catch (err) { + console.error("Failed to refresh grid data:", err); + } + } + + async saveChanges() { + const grid = $(this.gridRef.el).pqGrid(); + const updatedData = grid.pqGrid("option", "dataModel.data"); + + try { + await this.orm.call( + "hr.payslip.run", + "save_consolidated_attendance_data", + [this.state.payslipRunId, updatedData] + ); + await this.refreshDataAndView(); + this.showNotification("Changes saved successfully!"); + } catch (error) { + console.error("Error saving data:", error); + this.showNotification("Error saving changes!", "danger"); + } + } + + async recalculateLOP() { + try { + await this.orm.call( + "hr.payslip.run", + "recalculate_lop_days", + [this.state.payslipRunId] + ); + await this.refreshDataAndView(); + this.showNotification("LOP days recalculated successfully!"); + } catch (error) { + console.error("Error recalculating LOP:", error); + this.showNotification("Error recalculating LOP!", "danger"); + } + } + + async validateAll() { + try { + await this.orm.call( + "hr.payslip.run", + "validate_all_attendance_data", + [this.state.payslipRunId] + ); + await this.refreshDataAndView(); + this.showNotification("All records validated successfully!"); + } catch (error) { + console.error("Error validating records:", error); + this.showNotification("Error validating records!", "danger"); + } + } + + showNotification(message, type = "success") { + // Implement notification display (could use Odoo's notification service) + console.log(`${type}: ${message}`); + } + + async getColumns() { + const subCols = await this.getSubgridColumns(); + + return [ + { + title: "Employee", + dataIndx: "employee", + width: 200, + filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] }, + editable: false, + menuIcon: true, + menuInHide: true + }, + { + title: "Employee ID", + dataIndx: "employee_code", + width: 200, + filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] }, + editable: false, + menuInHide: true + }, + { + title: "Department", + dataIndx: "department", + width: 150, + filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] }, + editable: false + }, + { + title: "Date Of Joining", + dataIndx: "doj", + width: 100, + editable: false + }, + { + title: "Date Of Brith", + dataIndx: "birthday", + width: 100, + editable: false + }, + { + title: "Bank", + dataIndx: "bank", + width: 150, + editable: false + }, + { + title: "Total Days", + dataIndx: "total_days", + width: 80, + dataType: "integer", + editable: false + }, + { + title: "Worked Days", + dataIndx: "worked_days", + width: 90, + dataType: "integer", + editable: false + }, +// { +// title: "Attendance Days", +// dataIndx: "attendance_days", +// width: 100, +// dataType: "integer", +// editable: false +// }, + { + title: "Leave Days", + dataIndx: "leave_days", + width: 80, + dataType: "integer", + editable: false + }, + { + title: "LOP Days", + dataIndx: "lop_days", + width: 80, + dataType: "integer", + editable: (rowData) => rowData.state === 'draft', + summary: { type: "sum_" } + }, + { + title: "Sick Leave Balance", + dataIndx: "sick_leave_balance", + width: 100, + dataType: "float", + editable: false, + summary: { type: "sum_" }, + format: "##,##0.00" + }, + { + title: "Casual Leave Balance", + dataIndx: "casual_leave_balance", + width: 100, + dataType: "float", + editable: false, + summary: { type: "sum_" }, + format: "##,##0.00" + }, + { + title: "Privilege Leave Balance", + dataIndx: "privilege_leave_balance", + width: 100, + dataType: "float", + editable: false, + summary: { type: "sum_" }, + format: "##,##0.00" + }, +// { +// title: "Sick Leave Taken", +// dataIndx: "sick_leave_taken", +// width: 100, +// dataType: "float", +// editable: (rowData) => rowData.state === 'draft', +// summary: { type: "sum_" }, +// format: "##,##0.00" +// }, +// { +// title: "Casual Leave Taken", +// dataIndx: "casual_leave_taken", +// width: 100, +// dataType: "float", +// editable: (rowData) => rowData.state === 'draft', +// summary: { type: "sum_" }, +// format: "##,##0.00" +// }, +// { +// title: "Privilege Leave Taken", +// dataIndx: "privilege_leave_taken", +// width: 100, +// dataType: "float", +// editable: (rowData) => rowData.state === 'draft', +// summary: { type: "sum_" }, +// format: "##,##0.00" +// }, + { + title: "Status", + dataIndx: "state", + width: 80, + editable: false, + render: (ui) => { + const state = ui.rowData.state; + return `${state}`; + } + }, + ...subCols + ]; + } + + async getSubgridColumns() { + const response = await this.orm.call( + "hr.payslip.run", + "sub_columns", + [this.state.payslipRunId] + ); + return response || []; + } + + async refreshGrid() { + try { + await this.loadGridData(); + $(this.gridRef.el).pqGrid("refreshDataAndView"); + this.notification.add("Data refreshed successfully", { type: 'success' }); + } catch (error) { + console.error("Error refreshing grid:", error); + this.notification.add("Error refreshing data", { type: 'danger' }); + } + } + + async saveChanges() { + const grid = $(this.gridRef.el).pqGrid("instance"); + const updatedData = grid.option("dataModel.data"); + + try { + await this.orm.call( + "hr.payslip.run", + "save_consolidated_attendance_data", + [this.state.payslipRunId, updatedData] + ); + await this.refreshGrid(); + this.notification.add("Changes saved successfully", { type: 'success' }); + } catch (error) { + console.error("Error saving data:", error); + this.notification.add("Error saving changes", { type: 'danger' }); + } + } + + async recalculateLOP() { + try { + await this.orm.call( + "hr.payslip.run", + "recalculate_lop_days", + [this.state.payslipRunId] + ); + await this.refreshGrid(); + this.notification.add("LOP days recalculated successfully", { type: 'success' }); + } catch (error) { + console.error("Error recalculating LOP:", error); + this.notification.add("Error recalculating LOP", { type: 'danger' }); + } + } + + async validateAll() { + try { + await this.orm.call( + "hr.payslip.run", + "validate_all_attendance_data", + [this.state.payslipRunId] + ); + await this.refreshGrid(); + this.notification.add("All records validated successfully", { type: 'success' }); + } catch (error) { + console.error("Error validating records:", error); + this.notification.add("Error validating records", { type: 'danger' }); + } + } +} + +export const consolidatedPayslipGrid = { + component: ConsolidatedPayslipGrid, +}; + +registry.category("view_widgets").add("ConsolidatedPayslipGrid", consolidatedPayslipGrid); \ No newline at end of file diff --git a/addons_extensions/consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.xml b/addons_extensions/consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.xml new file mode 100644 index 000000000..df3dd93c7 --- /dev/null +++ b/addons_extensions/consolidated_batch_payslip/static/src/components/pqgrid_batch_payslip/pqgrid_batch_payslip.xml @@ -0,0 +1,6 @@ + + + +
+
+
diff --git a/addons_extensions/consolidated_batch_payslip/views/batch_payslip_view.xml b/addons_extensions/consolidated_batch_payslip/views/batch_payslip_view.xml new file mode 100644 index 000000000..944bf480f --- /dev/null +++ b/addons_extensions/consolidated_batch_payslip/views/batch_payslip_view.xml @@ -0,0 +1,18 @@ + + + hr.payslip.run.form.inherit.consolidated.pqgrid.owl + hr.payslip.run + + + + + +
+ +
+
+
+
+
+
+
diff --git a/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py b/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py index 5035f686a..aeab3e515 100644 --- a/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py +++ b/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py @@ -237,7 +237,7 @@ class BiometricDeviceDetails(models.Model): if fingerprint: fingerprint.finger_template = base64_data else: - self.env['hr.employee.fingerprint'].create({ + self.env['fingerprint.templates'].create({ 'finger_template': base64_data, 'finger_id': finger.fid, 'employee_bio_id': employee.id, From 8a7121937374002786514cabc0ecc5a951cbe750 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 02/49] Initial commit From b38d5f8baaed4e130ad5e67b58c36b87f93b380a Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 03/49] Initial commit From 9d51a837133cb02b5f272d5fdd39c538ccbc12ef Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 04/49] Initial commit From a47d0c16ca8b8f6fb540852f4e2562e320064760 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 05/49] Initial commit From 724f9560445c6ab4be9bdc496b844967215f5f0b Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 06/49] Initial commit From b41457f43bb835e9bc4bfd91d3e3661ed29b49d2 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 07/49] Initial commit From 6b47e358b747f97f179197dda4db4d07c750373d Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 08/49] Initial commit From 4dfc8067be0d4822d22fe8569d2612148c80dfce Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 09/49] Initial commit From 61ac808e568a2f2b494d28cdef7d71e9c3675851 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 10/49] Initial commit From c8567b353b778fde83f2f973f1986bb7789310af Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 11/49] Initial commit From f33bbee53a33d9b17d5598127a4d91cff0a1ce5f Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 12/49] Initial commit From 4e199c4fd95da973b84dc35ce5589002e56448af Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 13/49] Initial commit From b85061537187e1edb585b7e10a4e87f287344d76 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 14/49] Initial commit From 02bac1fe47bc9dd448f0a9a194c17e1cb171f1ba Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 15/49] Initial commit From 9495d2580a69a6cc9a35ad39a4ff7a6fda334779 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 16/49] Initial commit From 7b4b2bb6fca3cf2aee59d24767dcd93324d90243 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 17/49] Initial commit From e5b94079edcdac1dc0db15967a34371fdbf29cf2 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 18/49] Initial commit From 1d6d3f5e0ff93692e5c6be38a3e258b9c5697828 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 19/49] Initial commit From fab22d9949921dbe2b282775e3892df577fff6c6 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 20/49] Initial commit From b1e568de5599f3b5ef0859d2d730373e470fa72b Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 21/49] Initial commit From 7224c8a3d5adfe2447846b3fdd28564d62636ca8 Mon Sep 17 00:00:00 2001 From: Pranay Date: Mon, 24 Mar 2025 11:35:35 +0530 Subject: [PATCH 22/49] update whatsapp code --- addons_extensions/whatsapp/models/discuss_channel.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons_extensions/whatsapp/models/discuss_channel.py b/addons_extensions/whatsapp/models/discuss_channel.py index e2989c112..7fb18c9cf 100644 --- a/addons_extensions/whatsapp/models/discuss_channel.py +++ b/addons_extensions/whatsapp/models/discuss_channel.py @@ -201,8 +201,7 @@ class DiscussChannel(models.Model): subtype_xmlid='mail.mt_note', ) if partners_to_notify == channel.whatsapp_partner_id and wa_account_id.notify_user_ids.partner_id: - partners_to_notify += wa_account_id.notify_user_ids.partner_id - partners_to_notify = self.env['res.partner'].browse(list(set(partners_to_notify.ids))) + partners_to_notify |= wa_account_id.notify_user_ids.partner_id channel.channel_member_ids = [Command.clear()] + [Command.create({'partner_id': partner.id}) for partner in partners_to_notify] channel._broadcast(partners_to_notify.ids) return channel From 8bfc48b07c0243514489050d2fbd0d9fd4e1deaa Mon Sep 17 00:00:00 2001 From: Pranay Date: Mon, 24 Mar 2025 12:54:38 +0530 Subject: [PATCH 23/49] fix whatsapp --- addons_extensions/whatsapp/models/discuss_channel.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons_extensions/whatsapp/models/discuss_channel.py b/addons_extensions/whatsapp/models/discuss_channel.py index 7fb18c9cf..e2989c112 100644 --- a/addons_extensions/whatsapp/models/discuss_channel.py +++ b/addons_extensions/whatsapp/models/discuss_channel.py @@ -201,7 +201,8 @@ class DiscussChannel(models.Model): subtype_xmlid='mail.mt_note', ) if partners_to_notify == channel.whatsapp_partner_id and wa_account_id.notify_user_ids.partner_id: - partners_to_notify |= wa_account_id.notify_user_ids.partner_id + partners_to_notify += wa_account_id.notify_user_ids.partner_id + partners_to_notify = self.env['res.partner'].browse(list(set(partners_to_notify.ids))) channel.channel_member_ids = [Command.clear()] + [Command.create({'partner_id': partner.id}) for partner in partners_to_notify] channel._broadcast(partners_to_notify.ids) return channel From eeb1db9380365db2035e5f84d181aa04a5148fea Mon Sep 17 00:00:00 2001 From: Pranay Date: Mon, 24 Mar 2025 13:10:34 +0530 Subject: [PATCH 24/49] Recruitment Changes --- .../hr_recruitment_extended/models/hr_job_recruitment.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py b/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py index 97b114a76..d546a3cde 100644 --- a/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py +++ b/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py @@ -256,6 +256,8 @@ class HRJobRecruitment(models.Model): rec.submission_status = 'zero' + experience = fields.Many2one('candidate.experience', string="Experience") + @api.depends('application_ids.submitted_to_client') def _compute_no_of_submissions(self): counts = dict(self.env['hr.applicant']._read_group( From 1f66b6ed6b36b9503ab508fda2d33af377938bc1 Mon Sep 17 00:00:00 2001 From: Pranay Date: Mon, 7 Apr 2025 16:08:02 +0530 Subject: [PATCH 25/49] time-off FIX --- addons_extensions/hr_timeoff_extended/models/hr_timeoff.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py index 593c2a541..fea92e9e5 100644 --- a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py +++ b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py @@ -1,3 +1,4 @@ +from asyncore import write from calendar import month from dateutil.utils import today From 39928d886be24306808936656591402296da3d7f Mon Sep 17 00:00:00 2001 From: Pranay Date: Mon, 7 Apr 2025 16:34:42 +0530 Subject: [PATCH 26/49] TimeOff Fix --- addons_extensions/hr_timeoff_extended/models/hr_timeoff.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py index fea92e9e5..593c2a541 100644 --- a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py +++ b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py @@ -1,4 +1,3 @@ -from asyncore import write from calendar import month from dateutil.utils import today From 11e9b7ed86abd272dfad09ddeb9d4ce9f03788a0 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 27/49] Initial commit From e5b9e7183ca4cef2d14e2aca3bf12d880d1af73e Mon Sep 17 00:00:00 2001 From: administrator Date: Mon, 2 Jun 2025 15:19:52 +0530 Subject: [PATCH 28/49] pull commit --- addons_extensions/hr_employee_extended/__manifest__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons_extensions/hr_employee_extended/__manifest__.py b/addons_extensions/hr_employee_extended/__manifest__.py index ec27233ec..ee072e92c 100644 --- a/addons_extensions/hr_employee_extended/__manifest__.py +++ b/addons_extensions/hr_employee_extended/__manifest__.py @@ -18,8 +18,12 @@ 'version': '0.1', # any module necessary for this one to work correctly + 'depends': ['base','hr','account','mail','hr_skills', 'hr_contract'], + + + # always loaded 'data': [ 'security/security.xml', From 6b05e02a348af1f1a0b94ead544bfd493153d18c Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 29/49] Initial commit From 0c0b46f7a92b3dcdbdbe7b4af5dda192ca3d6099 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 30/49] Initial commit From a04cf80495f426561a0bbdbfcc3ce66535980aea Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 31/49] Initial commit From beaccbb23872ea8c087f6d6dfec728a3433beb08 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 32/49] Initial commit From 6582f3dc909c0852d06ab134a472f1aabbafb0c3 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 33/49] Initial commit From 69fabf5a5fcd736f67e3c6ba13a9f9dd75b80ff4 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 34/49] Initial commit From 4a69391d8ec952a1f6f52bf357faf6cd1c088cce Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 35/49] Initial commit From b8f860b8c9685457256b24a4a92b5961a369472d Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 36/49] Initial commit From b105ec91e08b8053959f8aa5039568ec65170c4d Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 37/49] Initial commit From e2c33eaf2a9266642721fdc9f5b86638e65b4a9d Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 38/49] Initial commit From 064fef323a2164285e0a85cb693b825745b870d4 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 39/49] Initial commit From 1bf17ff72511bc6c43afa4d8407b0788d7bdb734 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 40/49] Initial commit From ec471a356c44cff8e3bb2026f72281be3cc44f8e Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 41/49] Initial commit From 182e8068f8a7828a5d41a318a428ddd94e85beea Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 42/49] Initial commit From 91df8acb79acb90a9f7bb0ae46a7370cfb8e8832 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 43/49] Initial commit From 25a929bb41b71433de3028d127677c1813037347 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 44/49] Initial commit From dcb2eb15e7e2c0f35005727c8f15a07015a01187 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 45/49] Initial commit From f926c7377666053ff7a9a10446974cf8e2a26fb4 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 46/49] Initial commit From 291a64d7482d923ea4772e71c3d1f4ba39d3c41c Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 47/49] Initial commit From d74591ff580ad58ac44568fb2f8979ea72efbb58 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 48/49] Initial commit From 623f63d3bfa479cd34f08103e4b876ca7deccb05 Mon Sep 17 00:00:00 2001 From: administrator Date: Tue, 7 Jan 2025 09:29:28 +0530 Subject: [PATCH 49/49] Initial commit