diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7dd9bb1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,72 @@ +# Odoo Metatroncube — Project Context + +## Live Instance + +| Field | Value | +|----------|--------------------------------------------| +| URL | https://workplace.metatroncube.in/ | +| Database | `metatroncube_db` | +| User | `info@metatroncubesolutions.com` | +| Version | Odoo 17.0 (Community) | + +## MCP Connection + +The `.mcp.json` in this folder connects Claude to the live Odoo instance via `odoo-mcp`. +To connect at the start of a session run: + +``` +connect to metatroncube Odoo — url https://workplace.metatroncube.in/, db metatroncube_db, user info@metatroncubesolutions.com +``` + +Or use the `mcp__odoo-mcp__connect_database` tool with `instance_name: metatroncube`. + +## Local Docker Environment + +File: `docker-compose.yml` + +- Odoo 17.0 container → port `10001` (http://localhost:10001) +- PostgreSQL 15 +- Addons mounted at `./addons:/mnt/extra-addons` +- External volumes: + - `odoo-testing-addons_metatroncube_pgdata` + - `odoo-testing-addons_metatroncube_odoo_data` + +Start locally: +```bash +docker compose up -d +``` + +## Custom Addons + +All addons live in `./addons/`. Author: **Metatroncube Software Solutions**. + +| Module | Version | Purpose | Depends On | +|------------------------------|---------------|----------------------------------------------------------|-----------------------| +| `accounting_community` | 17.0.1.0.0 | Unlocks full accounting menus on Community Edition | `account` | +| `employee_documents` | — | Document management for employees | — | +| `mcs_appointment_booking` | 17.0.1.0.0 | Website appointment booking with calendar integration | `calendar`, `website` | +| `mcs_invoice_currency_display` | — | Currency display on invoices | — | +| `mcs_payroll_localization` | 17.0.1.0.0 | India & Canada payroll structures (OCA Payroll) | `payroll_account` | +| `payroll` | — | OCA Payroll base module | — | +| `payroll_account` | — | OCA Payroll + Accounting integration | `payroll` | + +## Key Custom Modules Detail + +### `mcs_appointment_booking` +- Full module: models, views, controllers, security +- Website-facing booking forms + backend calendar views +- License: AGPL-3 + +### `mcs_payroll_localization` +- India and Canada payroll structures +- Has a `post_init_hook` in `hooks.py` +- License: LGPL-3 + +### `accounting_community` +- By Antigravity (third-party, included in repo) +- Adds Journal Entries, Items, General Ledger, Partner Ledger to Community +- License: LGPL-3 + +## Snapshots + +Odoo MCP snapshots are stored in `./snapshots/` — used for rollback via `mcp__odoo-mcp__rollback_snapshot`. diff --git a/addons/c2c_payroll/__init__.py b/addons/c2c_payroll/__init__.py deleted file mode 100644 index 408a600..0000000 --- a/addons/c2c_payroll/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- -from . import models -from . import wizard diff --git a/addons/c2c_payroll/__manifest__.py b/addons/c2c_payroll/__manifest__.py deleted file mode 100644 index 36516ce..0000000 --- a/addons/c2c_payroll/__manifest__.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- 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 deleted file mode 100644 index 38f60c0..0000000 Binary files a/addons/c2c_payroll/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/addons/c2c_payroll/models/__init__.py b/addons/c2c_payroll/models/__init__.py deleted file mode 100644 index 8eb40c1..0000000 --- a/addons/c2c_payroll/models/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- 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 deleted file mode 100644 index 926540e..0000000 Binary files a/addons/c2c_payroll/models/__pycache__/__init__.cpython-310.pyc and /dev/null 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 deleted file mode 100644 index 2127d4c..0000000 Binary files a/addons/c2c_payroll/models/__pycache__/contract_extension.cpython-310.pyc and /dev/null differ diff --git a/addons/c2c_payroll/models/__pycache__/payslip.cpython-310.pyc b/addons/c2c_payroll/models/__pycache__/payslip.cpython-310.pyc deleted file mode 100644 index 40aa21f..0000000 Binary files a/addons/c2c_payroll/models/__pycache__/payslip.cpython-310.pyc and /dev/null 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 deleted file mode 100644 index 4a9139e..0000000 Binary files a/addons/c2c_payroll/models/__pycache__/salary_structure.cpython-310.pyc and /dev/null differ diff --git a/addons/c2c_payroll/models/contract_extension.py b/addons/c2c_payroll/models/contract_extension.py deleted file mode 100644 index 77a4716..0000000 --- a/addons/c2c_payroll/models/contract_extension.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- 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 deleted file mode 100644 index 47272d7..0000000 --- a/addons/c2c_payroll/models/payslip.py +++ /dev/null @@ -1,299 +0,0 @@ -# -*- 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 deleted file mode 100644 index 144c1cf..0000000 --- a/addons/c2c_payroll/models/salary_structure.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- 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 deleted file mode 100644 index 6e7e858..0000000 --- a/addons/c2c_payroll/reports/payslip_report.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - 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 deleted file mode 100644 index 64a7b28..0000000 --- a/addons/c2c_payroll/reports/payslip_report_template.xml +++ /dev/null @@ -1,184 +0,0 @@ - - - - diff --git a/addons/c2c_payroll/security/ir.model.access.csv b/addons/c2c_payroll/security/ir.model.access.csv deleted file mode 100644 index fe757a0..0000000 --- a/addons/c2c_payroll/security/ir.model.access.csv +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index f1ca0c6..0000000 --- a/addons/c2c_payroll/security/security.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - 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 deleted file mode 100644 index 513360f..0000000 --- a/addons/c2c_payroll/views/contract_views.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - hr.contract.form.inherit.c2c.payroll - hr.contract - - - - - - - - - - - - - - - - - - - diff --git a/addons/c2c_payroll/views/menu.xml b/addons/c2c_payroll/views/menu.xml deleted file mode 100644 index 51da1d1..0000000 --- a/addons/c2c_payroll/views/menu.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/addons/c2c_payroll/views/payslip_views.xml b/addons/c2c_payroll/views/payslip_views.xml deleted file mode 100644 index 9399df4..0000000 --- a/addons/c2c_payroll/views/payslip_views.xml +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - 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 deleted file mode 100644 index b1b9452..0000000 --- a/addons/c2c_payroll/views/salary_structure_views.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - 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 deleted file mode 100644 index 6aff706..0000000 --- a/addons/c2c_payroll/wizard/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- 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 deleted file mode 100644 index 7893fff..0000000 Binary files a/addons/c2c_payroll/wizard/__pycache__/__init__.cpython-310.pyc and /dev/null 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 deleted file mode 100644 index 231ad09..0000000 Binary files a/addons/c2c_payroll/wizard/__pycache__/payslip_generate_wizard.cpython-310.pyc and /dev/null differ diff --git a/addons/c2c_payroll/wizard/payslip_generate_wizard.py b/addons/c2c_payroll/wizard/payslip_generate_wizard.py deleted file mode 100644 index c210b05..0000000 --- a/addons/c2c_payroll/wizard/payslip_generate_wizard.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- 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', - }