From 17e870f96c9e18b945f8d37e7b5aa016a58af244 Mon Sep 17 00:00:00 2001 From: mohammed-alkhazrji Date: Tue, 8 Jul 2025 17:49:16 +0300 Subject: [PATCH] report and custody petty expense --- .../employee_custody_request/__manifest__.py | 3 +- .../models/__init__.py | 3 +- .../models/account_payment.py | 25 ---- .../models/hr_expense.py | 121 +++++++++++++++++- .../models/hr_expense_sheet.py | 98 ++++++++++++++ .../employee_custody_request/models/models.py | 22 ---- .../views/account_journal.xml | 1 - .../views/hr_expenes.xml | 16 +++ 8 files changed, 234 insertions(+), 55 deletions(-) create mode 100644 odex25_accounting/employee_custody_request/models/hr_expense_sheet.py create mode 100644 odex25_accounting/employee_custody_request/views/hr_expenes.xml diff --git a/odex25_accounting/employee_custody_request/__manifest__.py b/odex25_accounting/employee_custody_request/__manifest__.py index 2ddea4e33..9215f85ad 100644 --- a/odex25_accounting/employee_custody_request/__manifest__.py +++ b/odex25_accounting/employee_custody_request/__manifest__.py @@ -32,7 +32,8 @@ This course provides a comprehensive, hands-on guide to managing employee custod 'data/sequence.xml', 'views/account_journal.xml', 'views/types_custody.xml', - 'wizard/account_paymrnt_register_views.xml' + 'wizard/account_paymrnt_register_views.xml', + 'views/hr_expenes.xml' ], # only loaded in demonstration mode diff --git a/odex25_accounting/employee_custody_request/models/__init__.py b/odex25_accounting/employee_custody_request/models/__init__.py index 6db938bc0..a8ba66bd7 100644 --- a/odex25_accounting/employee_custody_request/models/__init__.py +++ b/odex25_accounting/employee_custody_request/models/__init__.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from . import models +from . import hr_expense_sheet from . import hr_expense from . import account_journal from . import types_custody -from . import account_payment \ No newline at end of file +from . import account_payment diff --git a/odex25_accounting/employee_custody_request/models/account_payment.py b/odex25_accounting/employee_custody_request/models/account_payment.py index bc0e4350a..1a86f988f 100644 --- a/odex25_accounting/employee_custody_request/models/account_payment.py +++ b/odex25_accounting/employee_custody_request/models/account_payment.py @@ -131,28 +131,3 @@ class AccountPayment(models.Model): print("its hr_request_pledge") return res - -# class AccountMove(models.Model): -# _inherit = 'account.move' -# -# def action_post(self): -# res = super(AccountMove, self).action_post() -# -# for move in self: -# if move.payment_id and move.payment_id.is_custody_journal and move.payment_id.custody_partner_id: -# -# custody_partner = move.payment_id.custody_partner_id -# -# -# employee = self.env['hr.employee'].search([ -# ('user_id.partner_id', '=', custody_partner.id) -# ], limit=1) -# -# if employee: -# self.env['hr.request.pledge'].allocate_payment_to_pledges( -# employee_id=employee.id, -# journal_id=move.journal_id.id, -# amount=move.amount -# ) -# -# return res diff --git a/odex25_accounting/employee_custody_request/models/hr_expense.py b/odex25_accounting/employee_custody_request/models/hr_expense.py index 7e12a3d1a..61d33bbf5 100644 --- a/odex25_accounting/employee_custody_request/models/hr_expense.py +++ b/odex25_accounting/employee_custody_request/models/hr_expense.py @@ -1,15 +1,126 @@ -from odoo import models +from odoo import api, fields, models, tools, _ +import ast +from odoo.exceptions import UserError, ValidationError +from odoo.tools import float_compare, float_is_zero class HrExpense(models.Model): _inherit = 'hr.expense' + payment_mode = fields.Selection( + selection_add=[ + ("custody", "Custody") + ] + ) + + def _create_sheet_from_expense_custody(self): + """Create expense sheet for custody mode""" + if any(expense.state != "draft" or expense.sheet_id for expense in self): + raise UserError(_("You cannot report twice the same line!")) + if len(self.mapped("employee_id")) != 1: + raise UserError(_("You cannot report expenses for different employees in the same report.")) + if any(not expense.product_id for expense in self): + raise UserError(_("You cannot create a report without product.")) + + ctx = self._context.copy() + sheet = ( + self.env["hr.expense.sheet"] + .with_context(ctx) + .create(self._prepare_expense_vals()) + ) + sheet._compute_from_employee_id() + return sheet + + def _create_sheet_from_expenses(self): + payment_mode = set(self.mapped("payment_mode")) + if len(payment_mode) > 1 and "petty_cash" in payment_mode: + raise UserError( + _("You cannot create report from many petty cash mode and other.") + ) + if all(expense.payment_mode == "petty_cash" for expense in self): + return self._create_sheet_from_expense_petty_cash() + if all(expense.payment_mode == "custody" for expense in self): + return self._create_sheet_from_expense_custody() + return super()._create_sheet_from_expenses() def _get_expense_account_destination(self): self.ensure_one() - if self.payment_mode == 'company_account' and self.sheet_id.account_payment_method_id: - account_dest = self.env['account.account'] - account_dest = self.sheet_id.account_payment_method_id.payment_account_id.id + + if self.payment_mode == 'company_account': + if self.sheet_id.account_payment_method_id: + account_dest = self.sheet_id.account_payment_method_id.payment_account_id.id + else: + if not self.sheet_id.bank_journal_id.payment_credit_account_id: + raise UserError( + _("No Outstanding Payments Account found for the %s journal, please configure one.") % ( + self.sheet_id.bank_journal_id.name)) + account_dest = self.sheet_id.bank_journal_id.payment_credit_account_id.id + + elif self.payment_mode == 'custody': + if self.sheet_id.account_payment_method_id: + account_dest = self.sheet_id.account_payment_method_id.payment_account_id.id + else: + if not self.sheet_id.custody_bank_journal_id.payment_credit_account_id: + raise UserError( + _("No Outstanding Payments Account found for the %s journal, please configure one.") % ( + self.sheet_id.custody_bank_journal_id.name)) + account_dest = self.sheet_id.custody_bank_journal_id.payment_credit_account_id.id + else: return super(HrExpense, self)._get_expense_account_destination() - return account_dest \ No newline at end of file + + return account_dest + + def _prepare_move_values(self): + """Override to handle custody payment mode like company_account""" + self.ensure_one() + + if self.payment_mode == 'company_account': + journal = self.sheet_id.bank_journal_id + elif self.payment_mode == 'custody': + journal = self.sheet_id.custody_bank_journal_id or self.sheet_id.bank_journal_id + else: + journal = self.sheet_id.journal_id + + account_date = self.sheet_id.accounting_date or self.date + move_values = { + 'journal_id': journal.id, + 'company_id': self.sheet_id.company_id.id, + 'date': account_date, + 'ref': self.sheet_id.name, + 'name': '/', + } + return move_values + + def action_move_create(self): + move_group_by_sheet = super().action_move_create() + + for expense in self: + if expense.payment_mode == 'custody': + move = expense.sheet_id.account_move_id + partner_id = expense.sheet_id.custody_partner_id.id if expense.sheet_id.custody_partner_id else \ + expense.employee_id.sudo().address_home_id.commercial_partner_id.id + amount =0 + for line in move.line_ids: + if line.credit > 0: + amount += line.credit + line.write({'partner_id': partner_id}) + if move.state == 'posted': + print("amount",amount) + employee = self.env['hr.employee'].sudo().search([ + ('user_id.partner_id', '=', partner_id) + ], limit=1) + + if employee: + + self.env['hr.request.pledge'].allocate_payment_to_pledges( + employee_id=employee.id, + journal_id=move.journal_id.id, + amount=amount + ) + + print(f"✅ posted {move.name} expense {expense.name}") + else: + print(f"❌ not posted {expense.name}") + + return move_group_by_sheet \ No newline at end of file diff --git a/odex25_accounting/employee_custody_request/models/hr_expense_sheet.py b/odex25_accounting/employee_custody_request/models/hr_expense_sheet.py new file mode 100644 index 000000000..8bfe68e26 --- /dev/null +++ b/odex25_accounting/employee_custody_request/models/hr_expense_sheet.py @@ -0,0 +1,98 @@ +from odoo import api, fields, models, tools, _ +import ast +from odoo.exceptions import UserError, ValidationError +from odoo.tools import float_compare, float_is_zero + + +class HrExpenseSheetPledge(models.Model): + _inherit = "hr.expense.sheet" + + available_account_payment_method_ids = fields.Many2many('account.payment.method.line', + compute="_compute_available_account_payment_method_ids") + account_payment_method_id = fields.Many2one('account.payment.method.line', default=False) + payment_account_id = fields.Many2one( + comodel_name='account.account', + check_company=True, + copy=False, + ondelete='restrict', ) + + custody_bank_journal_id = fields.Many2one( + 'account.journal', + string='Custody Journal', + domain=[('custody_journal', '=', True)], + ) + custody_partner_id = fields.Many2one('res.partner', string='Custody Partner') + + @api.depends('custody_bank_journal_id', 'bank_journal_id') + def _compute_available_account_payment_method_ids(self): + AccountPaymentMethodLine = self.env['account.payment.method.line'].sudo() + for rec in self: + if rec.payment_mode == 'custody' and rec.custody_bank_journal_id: + rec.available_account_payment_method_ids = AccountPaymentMethodLine.search( + [('id', 'in', rec.custody_bank_journal_id.outbound_payment_method_line_ids.ids)]) + elif rec.bank_journal_id: + rec.available_account_payment_method_ids = AccountPaymentMethodLine.search( + [('id', 'in', rec.bank_journal_id.outbound_payment_method_line_ids.ids)]) + else: + rec.available_account_payment_method_ids = False + + def action_submit_sheet(self): + for rec in self: + if rec.payment_mode == 'custody' and not rec.custody_partner_id: + raise UserError("عند اختيار نوع الدفع 'custody'، يجب تحديد 'Custody Partner' قبل الإرسال.") + return super(HrExpenseSheetPledge, self).action_submit_sheet() + + def _get_custody_partner_domain(self): + if not self.custody_bank_journal_id: + return [('id', '=', False)] + + pledge_requests = self.env['hr.request.pledge'].search([ + ('journal_id', '=', self.custody_bank_journal_id.id), + ('remaining_amount', '>', 0), + ]) + employee_ids = pledge_requests.mapped('employee_id') + partner_ids = [] + for employee in employee_ids: + if employee.user_id and employee.user_id.partner_id: + partner_ids.append(employee.user_id.partner_id.id) + if not partner_ids: + return [('id', '=', False)] + return [('id', 'in', partner_ids)] + + @api.onchange('payment_mode') + def _onchange_payment_mode(self): + """Reset fields when payment mode changes""" + if self.payment_mode == 'custody': + self.bank_journal_id = False + self.account_payment_method_id = False + self.custody_partner_id = False + else: + self.custody_bank_journal_id = False + self.custody_partner_id = False + + @api.onchange('custody_bank_journal_id') + def _onchange_custody_bank_journal_id(self): + if self.custody_bank_journal_id: + self.journal_id = self.custody_bank_journal_id + self.bank_journal_id = self.custody_bank_journal_id + + self.account_payment_method_id = False + self.custody_partner_id = False + return {'domain': {'custody_partner_id': self._get_custody_partner_domain()}} + + @api.onchange('bank_journal_id') + def _onchange_bank_journal_id(self): + self.account_payment_method_id = False + if self.payment_mode == 'custody': + self.custody_partner_id = False + return {'domain': {'custody_partner_id': self._get_custody_partner_domain()}} + + def action_sheet_move_create(self): + """Override to handle custody like company_account""" + res = super().action_sheet_move_create() + + custody_sheets = self.filtered(lambda sheet: sheet.payment_mode == 'custody' and sheet.expense_line_ids) + if custody_sheets: + custody_sheets.paid_expense_sheets() + + return res diff --git a/odex25_accounting/employee_custody_request/models/models.py b/odex25_accounting/employee_custody_request/models/models.py index 507caff8a..19dd907a9 100644 --- a/odex25_accounting/employee_custody_request/models/models.py +++ b/odex25_accounting/employee_custody_request/models/models.py @@ -317,28 +317,6 @@ class EmployeeJournal(models.Model): -class HrExpenseSheetPledge(models.Model): - _inherit = "hr.expense.sheet" - - bank_journal_id = fields.Many2one(default=False) - available_account_payment_method_ids = fields.Many2many('account.payment.method.line', compute="_compute_available_account_payment_method_ids") - account_payment_method_id = fields.Many2one('account.payment.method.line', default=False) - payment_account_id = fields.Many2one( - comodel_name='account.account', - check_company=True, - copy=False, - ondelete='restrict', ) - - - @api.depends('bank_journal_id') - def _compute_available_account_payment_method_ids(self): - AccountPaymentMethodLine = self.env['account.payment.method.line'].sudo() - for rec in self: - rec.available_account_payment_method_ids = AccountPaymentMethodLine.search([('id', 'in', self.bank_journal_id.outbound_payment_method_line_ids.ids)]) - - @api.onchange('bank_journal_id') - def _onchange_bank_journal_id(self): - self.account_payment_method_id = False # def action_sheet_move_create(self): # for sheet in self: diff --git a/odex25_accounting/employee_custody_request/views/account_journal.xml b/odex25_accounting/employee_custody_request/views/account_journal.xml index c5d6abc3a..2bd26c4e7 100644 --- a/odex25_accounting/employee_custody_request/views/account_journal.xml +++ b/odex25_accounting/employee_custody_request/views/account_journal.xml @@ -1,7 +1,6 @@ - account.journal.form.inherit account.journal diff --git a/odex25_accounting/employee_custody_request/views/hr_expenes.xml b/odex25_accounting/employee_custody_request/views/hr_expenes.xml new file mode 100644 index 000000000..993959862 --- /dev/null +++ b/odex25_accounting/employee_custody_request/views/hr_expenes.xml @@ -0,0 +1,16 @@ + + + hr.expense.sheet.form.inherit.custody + hr.expense.sheet + + + + + + + + + + + +