commit 78d5dc53ea1c6d6d3331d6852a990fe86a48a9ef Author: vidhubk Date: Thu Mar 5 17:08:54 2026 +0530 Theme Files Added diff --git a/addons/accounting_community/__init__.py b/addons/accounting_community/__init__.py new file mode 100644 index 0000000..737b477 --- /dev/null +++ b/addons/accounting_community/__init__.py @@ -0,0 +1 @@ +# from . import models diff --git a/addons/accounting_community/__manifest__.py b/addons/accounting_community/__manifest__.py new file mode 100644 index 0000000..327d927 --- /dev/null +++ b/addons/accounting_community/__manifest__.py @@ -0,0 +1,27 @@ +{ + 'name': 'Accounting Community', + 'version': '17.0.1.0.0', + 'summary': 'Full Accounting Features for Community Edition', + 'description': """ + Accounting Community + ==================== + This module unlocks the Full Accounting features in Odoo Community Edition. + + Features: + - Full Accounting Menu + - Journal Entries + - Journal Items + - General Ledger (Basic View) + - Partner Ledger (Basic View) + """, + 'category': 'Accounting/Accounting', + 'author': 'Antigravity', + 'depends': ['account'], + 'data': [ + 'views/account_menus.xml', + 'views/account_move_views.xml', + ], + 'installable': True, + 'application': False, + 'license': 'LGPL-3', +} diff --git a/addons/accounting_community/__pycache__/__init__.cpython-310.pyc b/addons/accounting_community/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..45e16bf Binary files /dev/null and b/addons/accounting_community/__pycache__/__init__.cpython-310.pyc differ diff --git a/addons/accounting_community/views/account_menus.xml b/addons/accounting_community/views/account_menus.xml new file mode 100644 index 0000000..3e79dfa --- /dev/null +++ b/addons/accounting_community/views/account_menus.xml @@ -0,0 +1,36 @@ + + + + + Accounting + + + + + + + + + + + + + + + + + diff --git a/addons/accounting_community/views/account_move_views.xml b/addons/accounting_community/views/account_move_views.xml new file mode 100644 index 0000000..6b35cf6 --- /dev/null +++ b/addons/accounting_community/views/account_move_views.xml @@ -0,0 +1,20 @@ + + + + + Journal Entries + account.move + tree,kanban,form + + + {'default_move_type': 'entry', 'search_default_misc_filter':1, 'view_no_maturity': True} + +

+ Create a journal entry +

+ A journal entry consists of several journal items, each of + which is either a debit or a credit. +

+
+
+
diff --git a/addons/accounting_community/views/account_views.xml b/addons/accounting_community/views/account_views.xml new file mode 100644 index 0000000..49431e3 --- /dev/null +++ b/addons/accounting_community/views/account_views.xml @@ -0,0 +1,4 @@ + + + + diff --git a/addons/c2c_payroll/__init__.py b/addons/c2c_payroll/__init__.py new file mode 100644 index 0000000..408a600 --- /dev/null +++ b/addons/c2c_payroll/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import models +from . import wizard diff --git a/addons/c2c_payroll/__manifest__.py b/addons/c2c_payroll/__manifest__.py new file mode 100644 index 0000000..36516ce --- /dev/null +++ b/addons/c2c_payroll/__manifest__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +{ + 'name': 'C2C Payroll', + 'version': '17.0.1.0.0', + 'category': 'Human Resources/Payroll', + 'summary': 'Enterprise-like Payroll for Odoo Community', + 'description': """ + Complete payroll solution for Odoo 17 Community Edition. + Features: + - Salary Structures (Basic, HRA, Allowances) + - Automatic PF, ESI, Professional Tax deductions + - Payslip generation with auto-computed net salary + - Accounting journal entry on payslip confirmation + - QWeb PDF payslip report + - Multi-company support + """, + 'author': 'C2C', + 'website': '', + 'license': 'LGPL-3', + 'depends': [ + 'hr', + 'hr_contract', + 'account', + ], + 'data': [ + 'security/security.xml', + 'security/ir.model.access.csv', + 'views/salary_structure_views.xml', + 'views/payslip_views.xml', + 'views/contract_views.xml', + 'views/menu.xml', + 'reports/payslip_report.xml', + 'reports/payslip_report_template.xml', + ], + 'installable': True, + 'application': True, + 'auto_install': False, +} diff --git a/addons/c2c_payroll/__pycache__/__init__.cpython-310.pyc b/addons/c2c_payroll/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..38f60c0 Binary files /dev/null and b/addons/c2c_payroll/__pycache__/__init__.cpython-310.pyc differ diff --git a/addons/c2c_payroll/models/__init__.py b/addons/c2c_payroll/models/__init__.py new file mode 100644 index 0000000..8eb40c1 --- /dev/null +++ b/addons/c2c_payroll/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from . import salary_structure +from . import contract_extension +from . import payslip diff --git a/addons/c2c_payroll/models/__pycache__/__init__.cpython-310.pyc b/addons/c2c_payroll/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..926540e Binary files /dev/null and b/addons/c2c_payroll/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/addons/c2c_payroll/models/__pycache__/contract_extension.cpython-310.pyc b/addons/c2c_payroll/models/__pycache__/contract_extension.cpython-310.pyc new file mode 100644 index 0000000..2127d4c Binary files /dev/null and b/addons/c2c_payroll/models/__pycache__/contract_extension.cpython-310.pyc differ diff --git a/addons/c2c_payroll/models/__pycache__/payslip.cpython-310.pyc b/addons/c2c_payroll/models/__pycache__/payslip.cpython-310.pyc new file mode 100644 index 0000000..40aa21f Binary files /dev/null and b/addons/c2c_payroll/models/__pycache__/payslip.cpython-310.pyc differ diff --git a/addons/c2c_payroll/models/__pycache__/salary_structure.cpython-310.pyc b/addons/c2c_payroll/models/__pycache__/salary_structure.cpython-310.pyc new file mode 100644 index 0000000..4a9139e Binary files /dev/null and b/addons/c2c_payroll/models/__pycache__/salary_structure.cpython-310.pyc differ diff --git a/addons/c2c_payroll/models/contract_extension.py b/addons/c2c_payroll/models/contract_extension.py new file mode 100644 index 0000000..77a4716 --- /dev/null +++ b/addons/c2c_payroll/models/contract_extension.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from odoo import fields, models + + +class ContractExtension(models.Model): + _inherit = 'hr.contract' + + salary_structure_id = fields.Many2one( + 'c2c.salary.structure', string='Salary Structure', + help='Salary structure used for payslip computation.', + ) + gross_salary = fields.Float( + string='Gross Salary', + help='Total gross salary (CTC) before deductions.', + ) + pf_applicable = fields.Boolean( + string='PF Applicable', default=True, + help='Whether Provident Fund deduction applies to this contract.', + ) + esi_applicable = fields.Boolean( + string='ESI Applicable', default=False, + help='Whether ESI deduction applies to this contract.', + ) diff --git a/addons/c2c_payroll/models/payslip.py b/addons/c2c_payroll/models/payslip.py new file mode 100644 index 0000000..47272d7 --- /dev/null +++ b/addons/c2c_payroll/models/payslip.py @@ -0,0 +1,299 @@ +# -*- coding: utf-8 -*- +from odoo import api, fields, models +from odoo.exceptions import UserError, ValidationError + + +class Payslip(models.Model): + _name = 'c2c.payslip' + _description = 'Employee Payslip' + _inherit = ['mail.thread', 'mail.activity.mixin'] + _order = 'date_from desc, id desc' + _rec_name = 'display_name' + + # ------------------------------------------------------------------ + # Core fields + # ------------------------------------------------------------------ + employee_id = fields.Many2one( + 'hr.employee', string='Employee', required=True, + tracking=True, + ) + contract_id = fields.Many2one( + 'hr.contract', string='Contract', required=True, + tracking=True, + domain="[('employee_id', '=', employee_id), ('state', '=', 'open')]", + ) + date_from = fields.Date( + string='Period From', required=True, + ) + date_to = fields.Date( + string='Period To', required=True, + ) + company_id = fields.Many2one( + 'res.company', string='Company', required=True, + default=lambda self: self.env.company, + ) + + # ------------------------------------------------------------------ + # Earnings (computed & stored) + # ------------------------------------------------------------------ + gross_salary = fields.Float( + string='Gross Salary', compute='_compute_salary', store=True, + ) + basic = fields.Float( + string='Basic', compute='_compute_salary', store=True, + ) + hra = fields.Float( + string='HRA', compute='_compute_salary', store=True, + ) + allowances = fields.Float( + string='Allowances', compute='_compute_salary', store=True, + ) + + # ------------------------------------------------------------------ + # Deductions (computed & stored) + # ------------------------------------------------------------------ + pf_deduction = fields.Float( + string='PF Deduction', compute='_compute_salary', store=True, + ) + esi_deduction = fields.Float( + string='ESI Deduction', compute='_compute_salary', store=True, + ) + professional_tax = fields.Float( + string='Professional Tax', compute='_compute_salary', store=True, + ) + total_deductions = fields.Float( + string='Total Deductions', compute='_compute_salary', store=True, + ) + + # ------------------------------------------------------------------ + # Net + # ------------------------------------------------------------------ + net_salary = fields.Float( + string='Net Salary', compute='_compute_salary', store=True, + ) + + # ------------------------------------------------------------------ + # State & Accounting + # ------------------------------------------------------------------ + state = fields.Selection([ + ('draft', 'Draft'), + ('confirmed', 'Confirmed'), + ('paid', 'Paid'), + ], string='Status', default='draft', tracking=True, copy=False) + + journal_entry_id = fields.Many2one( + 'account.move', string='Journal Entry', readonly=True, copy=False, + ) + journal_entry_count = fields.Integer( + compute='_compute_journal_entry_count', + ) + + # ------------------------------------------------------------------ + # Display name + # ------------------------------------------------------------------ + display_name = fields.Char(compute='_compute_display_name', store=True) + + @api.depends('employee_id', 'date_from', 'date_to') + def _compute_display_name(self): + for rec in self: + emp_name = rec.employee_id.name or 'New' + date_from = rec.date_from or '' + date_to = rec.date_to or '' + rec.display_name = '%s (%s - %s)' % (emp_name, date_from, date_to) + + # ------------------------------------------------------------------ + # Computed salary + # ------------------------------------------------------------------ + @api.depends( + 'contract_id', + 'contract_id.gross_salary', + 'contract_id.salary_structure_id', + 'contract_id.salary_structure_id.basic_percentage', + 'contract_id.salary_structure_id.hra_percentage', + 'contract_id.salary_structure_id.allowance_percentage', + 'contract_id.salary_structure_id.pf_percentage', + 'contract_id.salary_structure_id.esi_percentage', + 'contract_id.salary_structure_id.professional_tax_fixed', + 'contract_id.pf_applicable', + 'contract_id.esi_applicable', + ) + def _compute_salary(self): + for rec in self: + contract = rec.contract_id + structure = contract.salary_structure_id if contract else False + + if not contract or not structure: + rec.gross_salary = 0.0 + rec.basic = 0.0 + rec.hra = 0.0 + rec.allowances = 0.0 + rec.pf_deduction = 0.0 + rec.esi_deduction = 0.0 + rec.professional_tax = 0.0 + rec.total_deductions = 0.0 + rec.net_salary = 0.0 + continue + + gross = contract.gross_salary + rec.gross_salary = gross + + # Earnings + rec.basic = gross * structure.basic_percentage / 100.0 + rec.hra = gross * structure.hra_percentage / 100.0 + rec.allowances = gross * structure.allowance_percentage / 100.0 + + # Deductions + pf = (rec.basic * structure.pf_percentage / 100.0) if contract.pf_applicable else 0.0 + esi = (gross * structure.esi_percentage / 100.0) if contract.esi_applicable else 0.0 + pt = structure.professional_tax_fixed + + rec.pf_deduction = pf + rec.esi_deduction = esi + rec.professional_tax = pt + rec.total_deductions = pf + esi + pt + rec.net_salary = gross - rec.total_deductions + + # ------------------------------------------------------------------ + # Journal entry count + # ------------------------------------------------------------------ + @api.depends('journal_entry_id') + def _compute_journal_entry_count(self): + for rec in self: + rec.journal_entry_count = 1 if rec.journal_entry_id else 0 + + # ------------------------------------------------------------------ + # Onchange helpers + # ------------------------------------------------------------------ + @api.onchange('employee_id') + def _onchange_employee_id(self): + """Auto-fill contract when employee changes.""" + if self.employee_id: + contract = self.env['hr.contract'].search([ + ('employee_id', '=', self.employee_id.id), + ('state', '=', 'open'), + ('company_id', '=', self.env.company.id), + ], limit=1) + self.contract_id = contract + else: + self.contract_id = False + + # ------------------------------------------------------------------ + # Constraints + # ------------------------------------------------------------------ + @api.constrains('date_from', 'date_to') + def _check_dates(self): + for rec in self: + if rec.date_from and rec.date_to and rec.date_from > rec.date_to: + raise ValidationError('Period From cannot be after Period To.') + + # ------------------------------------------------------------------ + # Actions + # ------------------------------------------------------------------ + def action_confirm(self): + """Confirm the payslip and create an accounting journal entry.""" + for rec in self: + if rec.state != 'draft': + raise UserError('Only draft payslips can be confirmed.') + if not rec.contract_id or not rec.contract_id.salary_structure_id: + raise UserError( + 'Please set a salary structure on the contract before confirming.' + ) + if rec.net_salary <= 0: + raise UserError('Net salary must be greater than zero to confirm.') + + # Get accounts from system parameters (company-specific) + ICP = self.env['ir.config_parameter'].sudo() + expense_account_id = int( + ICP.get_param('c2c_payroll.salary_expense_account_id', default=0) + ) + payable_account_id = int( + ICP.get_param('c2c_payroll.salary_payable_account_id', default=0) + ) + + if not expense_account_id or not payable_account_id: + raise UserError( + 'Please configure Salary Expense and Payable accounts in ' + 'Settings → Technical → Parameters → System Parameters.\n\n' + 'Keys:\n' + ' • c2c_payroll.salary_expense_account_id\n' + ' • c2c_payroll.salary_payable_account_id' + ) + + # Validate accounts exist + expense_account = self.env['account.account'].browse(expense_account_id) + payable_account = self.env['account.account'].browse(payable_account_id) + if not expense_account.exists() or not payable_account.exists(): + raise UserError( + 'Configured salary accounts do not exist. ' + 'Please verify the system parameter values.' + ) + + journal = self.env['account.journal'].search([ + ('type', '=', 'general'), + ('company_id', '=', rec.company_id.id), + ], limit=1) + if not journal: + raise UserError( + 'No general journal found for company %s.' % rec.company_id.name + ) + + move_vals = { + 'journal_id': journal.id, + 'date': rec.date_to, + 'ref': 'Payslip: %s' % rec.display_name, + 'company_id': rec.company_id.id, + 'line_ids': [ + (0, 0, { + 'name': 'Salary Expense - %s' % rec.employee_id.name, + 'account_id': expense_account_id, + 'debit': rec.net_salary, + 'credit': 0.0, + }), + (0, 0, { + 'name': 'Employee Payable - %s' % rec.employee_id.name, + 'account_id': payable_account_id, + 'debit': 0.0, + 'credit': rec.net_salary, + }), + ], + } + move = self.env['account.move'].create(move_vals) + rec.write({ + 'state': 'confirmed', + 'journal_entry_id': move.id, + }) + + def action_mark_paid(self): + """Mark confirming payslip as paid.""" + for rec in self: + if rec.state != 'confirmed': + raise UserError('Only confirmed payslips can be marked as paid.') + rec.write({'state': 'paid'}) + + def action_reset_to_draft(self): + """Reset to draft (deletes journal entry if unposted).""" + for rec in self: + if rec.state == 'paid': + raise UserError('Paid payslips cannot be reset to draft.') + if rec.journal_entry_id: + if rec.journal_entry_id.state == 'posted': + raise UserError( + 'Cannot reset: the journal entry is already posted. ' + 'Please cancel it first.' + ) + rec.journal_entry_id.unlink() + rec.write({'state': 'draft', 'journal_entry_id': False}) + + def action_open_journal_entry(self): + """Smart button to open the linked journal entry.""" + self.ensure_one() + if not self.journal_entry_id: + raise UserError('No journal entry linked to this payslip.') + return { + 'type': 'ir.actions.act_window', + 'name': 'Journal Entry', + 'res_model': 'account.move', + 'res_id': self.journal_entry_id.id, + 'view_mode': 'form', + 'target': 'current', + } diff --git a/addons/c2c_payroll/models/salary_structure.py b/addons/c2c_payroll/models/salary_structure.py new file mode 100644 index 0000000..144c1cf --- /dev/null +++ b/addons/c2c_payroll/models/salary_structure.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +from odoo import api, fields, models + + +class SalaryStructure(models.Model): + _name = 'c2c.salary.structure' + _description = 'Salary Structure' + _rec_name = 'name' + + name = fields.Char(string='Name', required=True) + basic_percentage = fields.Float( + string='Basic (%)', required=True, default=50.0, + help='Percentage of gross salary allocated as Basic.', + ) + hra_percentage = fields.Float( + string='HRA (%)', required=True, default=20.0, + help='Percentage of gross salary allocated as HRA.', + ) + allowance_percentage = fields.Float( + string='Allowances (%)', required=True, default=30.0, + help='Percentage of gross salary allocated as Allowances.', + ) + pf_percentage = fields.Float( + string='PF (%)', required=True, default=12.0, + help='Provident Fund deduction percentage on Basic salary.', + ) + esi_percentage = fields.Float( + string='ESI (%)', required=True, default=1.75, + help='ESI deduction percentage on Gross salary.', + ) + professional_tax_fixed = fields.Float( + string='Professional Tax (Fixed)', default=200.0, + help='Fixed professional tax amount deducted per month.', + ) + company_id = fields.Many2one( + 'res.company', string='Company', required=True, + default=lambda self: self.env.company, + ) + active = fields.Boolean(default=True) + + @api.constrains('basic_percentage', 'hra_percentage', 'allowance_percentage') + def _check_percentages(self): + for rec in self: + total = rec.basic_percentage + rec.hra_percentage + rec.allowance_percentage + if abs(total - 100.0) > 0.01: + raise models.ValidationError( + 'Basic + HRA + Allowances percentages must equal 100%%. ' + 'Current total: %.2f%%' % total + ) diff --git a/addons/c2c_payroll/reports/payslip_report.xml b/addons/c2c_payroll/reports/payslip_report.xml new file mode 100644 index 0000000..6e7e858 --- /dev/null +++ b/addons/c2c_payroll/reports/payslip_report.xml @@ -0,0 +1,15 @@ + + + + + + + Payslip + c2c.payslip + qweb-pdf + c2c_payroll.report_payslip_document + c2c_payroll.report_payslip_document + + report + + diff --git a/addons/c2c_payroll/reports/payslip_report_template.xml b/addons/c2c_payroll/reports/payslip_report_template.xml new file mode 100644 index 0000000..64a7b28 --- /dev/null +++ b/addons/c2c_payroll/reports/payslip_report_template.xml @@ -0,0 +1,184 @@ + + + + diff --git a/addons/c2c_payroll/security/ir.model.access.csv b/addons/c2c_payroll/security/ir.model.access.csv new file mode 100644 index 0000000..fe757a0 --- /dev/null +++ b/addons/c2c_payroll/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_salary_structure_user,c2c.salary.structure.user,model_c2c_salary_structure,c2c_payroll.group_payroll_user,1,0,0,0 +access_salary_structure_manager,c2c.salary.structure.manager,model_c2c_salary_structure,c2c_payroll.group_payroll_manager,1,1,1,1 +access_payslip_user,c2c.payslip.user,model_c2c_payslip,c2c_payroll.group_payroll_user,1,1,1,0 +access_payslip_manager,c2c.payslip.manager,model_c2c_payslip,c2c_payroll.group_payroll_manager,1,1,1,1 +access_payslip_wizard_user,c2c.payslip.generate.wizard.user,model_c2c_payslip_generate_wizard,c2c_payroll.group_payroll_user,1,1,1,1 +access_payslip_wizard_manager,c2c.payslip.generate.wizard.manager,model_c2c_payslip_generate_wizard,c2c_payroll.group_payroll_manager,1,1,1,1 diff --git a/addons/c2c_payroll/security/security.xml b/addons/c2c_payroll/security/security.xml new file mode 100644 index 0000000..f1ca0c6 --- /dev/null +++ b/addons/c2c_payroll/security/security.xml @@ -0,0 +1,43 @@ + + + + + + + C2C Payroll + Access rights for C2C Payroll module. + 30 + + + + + + + Payroll User + + + + + + Payroll Manager + + + + + + + + + Payslip: multi-company + + [('company_id', 'in', company_ids)] + + + + + Salary Structure: multi-company + + [('company_id', 'in', company_ids)] + + + diff --git a/addons/c2c_payroll/views/contract_views.xml b/addons/c2c_payroll/views/contract_views.xml new file mode 100644 index 0000000..513360f --- /dev/null +++ b/addons/c2c_payroll/views/contract_views.xml @@ -0,0 +1,27 @@ + + + + + + + hr.contract.form.inherit.c2c.payroll + hr.contract + + + + + + + + + + + + + + + + + + + diff --git a/addons/c2c_payroll/views/menu.xml b/addons/c2c_payroll/views/menu.xml new file mode 100644 index 0000000..51da1d1 --- /dev/null +++ b/addons/c2c_payroll/views/menu.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + diff --git a/addons/c2c_payroll/views/payslip_views.xml b/addons/c2c_payroll/views/payslip_views.xml new file mode 100644 index 0000000..9399df4 --- /dev/null +++ b/addons/c2c_payroll/views/payslip_views.xml @@ -0,0 +1,191 @@ + + + + + + + c2c.payslip.tree + c2c.payslip + + + + + + + + + + + + + + + + + + + c2c.payslip.form + c2c.payslip + +
+
+
+ +
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + + + + + c2c.payslip.search + c2c.payslip + + + + + + + + + + + + + + + + + + + Payslips + c2c.payslip + tree,form + +

+ No payslips yet +

+

+ Create payslips individually or use the "Generate Payslips" wizard + to create them in bulk for all employees with active contracts. +

+
+
+ + + + + + c2c.payslip.generate.wizard.form + c2c.payslip.generate.wizard + +
+ + + + + +
+
+
+
+
+ + + Generate Payslips + c2c.payslip.generate.wizard + form + new + +
diff --git a/addons/c2c_payroll/views/salary_structure_views.xml b/addons/c2c_payroll/views/salary_structure_views.xml new file mode 100644 index 0000000..b1b9452 --- /dev/null +++ b/addons/c2c_payroll/views/salary_structure_views.xml @@ -0,0 +1,76 @@ + + + + + + + c2c.salary.structure.tree + c2c.salary.structure + + + + + + + + + + + + + + + + + + + c2c.salary.structure.form + c2c.salary.structure + +
+ +
+
+ + + + + + + + + + + + + + + + +
+
+
+
+ + + + + + Salary Structures + c2c.salary.structure + tree,form + +

+ Create your first Salary Structure +

+

+ Define how gross salary is split into Basic, HRA, and Allowances, + and configure PF, ESI, and Professional Tax deduction rules. +

+
+
+
diff --git a/addons/c2c_payroll/wizard/__init__.py b/addons/c2c_payroll/wizard/__init__.py new file mode 100644 index 0000000..6aff706 --- /dev/null +++ b/addons/c2c_payroll/wizard/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import payslip_generate_wizard diff --git a/addons/c2c_payroll/wizard/__pycache__/__init__.cpython-310.pyc b/addons/c2c_payroll/wizard/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7893fff Binary files /dev/null and b/addons/c2c_payroll/wizard/__pycache__/__init__.cpython-310.pyc differ diff --git a/addons/c2c_payroll/wizard/__pycache__/payslip_generate_wizard.cpython-310.pyc b/addons/c2c_payroll/wizard/__pycache__/payslip_generate_wizard.cpython-310.pyc new file mode 100644 index 0000000..231ad09 Binary files /dev/null and b/addons/c2c_payroll/wizard/__pycache__/payslip_generate_wizard.cpython-310.pyc differ diff --git a/addons/c2c_payroll/wizard/payslip_generate_wizard.py b/addons/c2c_payroll/wizard/payslip_generate_wizard.py new file mode 100644 index 0000000..c210b05 --- /dev/null +++ b/addons/c2c_payroll/wizard/payslip_generate_wizard.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class PayslipGenerateWizard(models.TransientModel): + _name = 'c2c.payslip.generate.wizard' + _description = 'Generate Payslips Wizard' + + date_from = fields.Date(string='Period From', required=True) + date_to = fields.Date(string='Period To', required=True) + company_id = fields.Many2one( + 'res.company', string='Company', required=True, + default=lambda self: self.env.company, + ) + + def action_generate_payslips(self): + """Generate payslips for all employees with active contracts.""" + self.ensure_one() + + if self.date_from > self.date_to: + raise UserError('Period From cannot be after Period To.') + + contracts = self.env['hr.contract'].search([ + ('state', '=', 'open'), + ('company_id', '=', self.company_id.id), + ('salary_structure_id', '!=', False), + ('gross_salary', '>', 0), + ]) + + if not contracts: + raise UserError( + 'No active contracts found with a salary structure and gross salary ' + 'for company %s.' % self.company_id.name + ) + + Payslip = self.env['c2c.payslip'] + created_payslips = Payslip + + for contract in contracts: + # Skip if payslip already exists for this employee and period + existing = Payslip.search([ + ('employee_id', '=', contract.employee_id.id), + ('date_from', '=', self.date_from), + ('date_to', '=', self.date_to), + ('company_id', '=', self.company_id.id), + ], limit=1) + if existing: + continue + + payslip = Payslip.create({ + 'employee_id': contract.employee_id.id, + 'contract_id': contract.id, + 'date_from': self.date_from, + 'date_to': self.date_to, + 'company_id': self.company_id.id, + }) + created_payslips |= payslip + + if not created_payslips: + raise UserError( + 'All eligible employees already have payslips for the selected period.' + ) + + return { + 'type': 'ir.actions.act_window', + 'name': 'Generated Payslips', + 'res_model': 'c2c.payslip', + 'view_mode': 'tree,form', + 'domain': [('id', 'in', created_payslips.ids)], + 'target': 'current', + } diff --git a/addons/employee_documents/__init__.py b/addons/employee_documents/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/addons/employee_documents/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/addons/employee_documents/__manifest__.py b/addons/employee_documents/__manifest__.py new file mode 100644 index 0000000..7832049 --- /dev/null +++ b/addons/employee_documents/__manifest__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +{ + 'name': 'Employee Documents', + 'version': '1.0', + 'category': 'Human Resources', + 'summary': 'Manage Employee Documents', + 'description': """ + This module allows you to manage employee documents such as Offer Letters, Contracts, ID Proofs, etc. + It adds a documents tab in the Employee form view. + """, + 'author': 'Antigravity', + 'depends': ['hr'], + 'data': [ + 'security/security.xml', + 'security/ir.model.access.csv', + 'views/employee_document_views.xml', + 'views/hr_employee_views.xml', + ], + 'installable': True, + 'application': True, + 'license': 'LGPL-3', +} diff --git a/addons/employee_documents/__pycache__/__init__.cpython-310.pyc b/addons/employee_documents/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ab04e4d Binary files /dev/null and b/addons/employee_documents/__pycache__/__init__.cpython-310.pyc differ diff --git a/addons/employee_documents/models/__init__.py b/addons/employee_documents/models/__init__.py new file mode 100644 index 0000000..a996134 --- /dev/null +++ b/addons/employee_documents/models/__init__.py @@ -0,0 +1,2 @@ +from . import employee_document +from . import hr_employee diff --git a/addons/employee_documents/models/__pycache__/__init__.cpython-310.pyc b/addons/employee_documents/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2320b84 Binary files /dev/null and b/addons/employee_documents/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/addons/employee_documents/models/__pycache__/employee_document.cpython-310.pyc b/addons/employee_documents/models/__pycache__/employee_document.cpython-310.pyc new file mode 100644 index 0000000..65f5883 Binary files /dev/null and b/addons/employee_documents/models/__pycache__/employee_document.cpython-310.pyc differ diff --git a/addons/employee_documents/models/__pycache__/hr_employee.cpython-310.pyc b/addons/employee_documents/models/__pycache__/hr_employee.cpython-310.pyc new file mode 100644 index 0000000..4a0c21f Binary files /dev/null and b/addons/employee_documents/models/__pycache__/hr_employee.cpython-310.pyc differ diff --git a/addons/employee_documents/models/employee_document.py b/addons/employee_documents/models/employee_document.py new file mode 100644 index 0000000..e140b7b --- /dev/null +++ b/addons/employee_documents/models/employee_document.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api +from datetime import datetime + +class EmployeeDocument(models.Model): + _name = 'employee.document' + _description = 'Employee Document' + + name = fields.Char(string='Document Reference', required=True) + employee_id = fields.Many2one('hr.employee', string='Employee', required=True) + document_type = fields.Selection([ + ('offer_letter', 'Offer Letter'), + ('contract', 'Contract'), + ('id_proof', 'ID Proof'), + ('other', 'Other'), + ], string='Document Type', default='other', required=True) + file = fields.Binary(string='File', required=True) + file_name = fields.Char(string='File Name') + upload_date = fields.Datetime(string='Upload Date', default=fields.Datetime.now) diff --git a/addons/employee_documents/models/hr_employee.py b/addons/employee_documents/models/hr_employee.py new file mode 100644 index 0000000..94cbe50 --- /dev/null +++ b/addons/employee_documents/models/hr_employee.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields + +class HrEmployee(models.Model): + _inherit = 'hr.employee' + + document_ids = fields.One2many('employee.document', 'employee_id', string='Documents') diff --git a/addons/employee_documents/security/ir.model.access.csv b/addons/employee_documents/security/ir.model.access.csv new file mode 100644 index 0000000..79a451c --- /dev/null +++ b/addons/employee_documents/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_employee_document_user,employee.document.user,model_employee_document,base.group_user,1,0,0,0 +access_employee_document_manager,employee.document.manager,model_employee_document,employee_documents.group_hr_documents,1,1,1,1 diff --git a/addons/employee_documents/security/security.xml b/addons/employee_documents/security/security.xml new file mode 100644 index 0000000..a7b016f --- /dev/null +++ b/addons/employee_documents/security/security.xml @@ -0,0 +1,10 @@ + + + + + HR Documents + + + + + diff --git a/addons/employee_documents/views/employee_document_views.xml b/addons/employee_documents/views/employee_document_views.xml new file mode 100644 index 0000000..c02e408 --- /dev/null +++ b/addons/employee_documents/views/employee_document_views.xml @@ -0,0 +1,78 @@ + + + + + + employee.document.tree + employee.document + + + + + + + + + + + + + employee.document.form + employee.document + +
+ + + + + + + + + + + + + + +
+
+
+ + + + employee.document.search + employee.document + + + + + + + + + + + + + + + + Documents + employee.document + tree,form + +

+ Create your first employee document +

+
+
+ + + + +
diff --git a/addons/employee_documents/views/hr_employee_views.xml b/addons/employee_documents/views/hr_employee_views.xml new file mode 100644 index 0000000..f6c7444 --- /dev/null +++ b/addons/employee_documents/views/hr_employee_views.xml @@ -0,0 +1,23 @@ + + + + hr.employee.form.inherit.document + hr.employee + + + + + + + + + + + + + + + + + + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8c0709f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +version: "3.8" + +services: + db: + image: postgres:15 + container_name: odoo_metatroncube_db + environment: + POSTGRES_DB: postgres + POSTGRES_USER: odoo + POSTGRES_PASSWORD: odoo + volumes: + - client1_pgdata:/var/lib/postgresql/data + restart: always + + odoo: + image: odoo:17.0 + container_name: odoo_metatroncube + depends_on: + - db + ports: + - "10101:8069" + environment: + HOST: db + USER: odoo + PASSWORD: odoo + volumes: + - client1_odoo_data:/var/lib/odoo + - ./addons:/mnt/extra-addons + restart: always + +volumes: + client1_pgdata: + client1_odoo_data: \ No newline at end of file