135 lines
6.5 KiB
Python
135 lines
6.5 KiB
Python
from odoo import api, fields, models, _, Command
|
|
from odoo.exceptions import UserError, ValidationError
|
|
|
|
class HrExpenseRealization(models.Model):
|
|
_name = 'hr.expense.realization'
|
|
_description = 'Expense Realization'
|
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
|
_order = 'date desc, id desc'
|
|
|
|
name = fields.Char(string='Reference', required=True, copy=False, default=lambda self: _('New'))
|
|
expense_id = fields.Many2one(
|
|
'hr.expense',
|
|
string='Source Expense',
|
|
required=True,
|
|
domain="[('payment_mode', '=', 'company_account')]",
|
|
ondelete='cascade'
|
|
)
|
|
employee_id = fields.Many2one('hr.employee', string='Employee', related='expense_id.employee_id', store=True)
|
|
company_id = fields.Many2one('res.company', string='Company', related='expense_id.company_id', store=True)
|
|
currency_id = fields.Many2one('res.currency', string='Currency', related='expense_id.currency_id', store=True)
|
|
date = fields.Date(string='Date', default=fields.Date.context_today, required=True)
|
|
description = fields.Text(string='Description')
|
|
|
|
line_ids = fields.One2many('hr.expense.realization.line', 'realization_id', string='Receipt Lines')
|
|
total_amount = fields.Monetary(string='Total Amount', compute='_compute_total_amount', store=True, currency_field='currency_id')
|
|
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('confirmed', 'Confirmed'),
|
|
('posted', 'Posted')
|
|
], string='Status', default='draft', tracking=True)
|
|
|
|
default_counterpart_account_id = fields.Many2one('account.account', string='Default Counterpart Account', tracking=True, groups="account.group_account_invoice")
|
|
journal_id = fields.Many2one('account.journal', string='Journal', tracking=True, groups="account.group_account_invoice", default=lambda self: self.env['account.journal'].search([('name', '=', 'Realisasi')], limit=1))
|
|
move_id = fields.Many2one('account.move', string='Journal Entry', readonly=True, groups="account.group_account_invoice", help="Reference to the first journal entry created.")
|
|
|
|
@api.depends('line_ids.amount')
|
|
def _compute_total_amount(self):
|
|
for rec in self:
|
|
rec.total_amount = sum(rec.line_ids.mapped('amount'))
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
for vals in vals_list:
|
|
if vals.get('name', _('New')) == _('New'):
|
|
vals['name'] = self.env['ir.sequence'].next_by_code('hr.expense.realization') or _('New')
|
|
return super().create(vals_list)
|
|
|
|
def action_confirm(self):
|
|
self.ensure_one()
|
|
if not self.line_ids:
|
|
raise UserError(_("Please add at least one receipt line."))
|
|
self.state = 'confirmed'
|
|
if self.expense_id:
|
|
self.expense_id.write({'receipt_received': True})
|
|
# Explicitly trigger recompute of the sheet status
|
|
if self.expense_id.sheet_id:
|
|
self.expense_id.sheet_id._compute_receipt_status()
|
|
|
|
def action_apply_default_account(self):
|
|
self.ensure_one()
|
|
if not self.default_counterpart_account_id:
|
|
raise UserError(_("Please set a Default Counterpart Account first."))
|
|
|
|
for line in self.line_ids:
|
|
if not line.counterpart_account_id:
|
|
line.counterpart_account_id = self.default_counterpart_account_id
|
|
|
|
def action_post(self):
|
|
self.ensure_one()
|
|
if self.state != 'confirmed':
|
|
raise UserError(_("Only confirmed realizations can be posted."))
|
|
if not self.journal_id:
|
|
raise UserError(_("Please specify the Journal before posting."))
|
|
|
|
# Determine the Expense Account
|
|
product = self.expense_id.product_id.with_company(self.company_id)
|
|
expense_account = product.property_account_expense_company_id or product.property_account_expense_id
|
|
if not expense_account:
|
|
expense_account = self.env['ir.property']._get('property_account_expense_categ_id', 'product.category')
|
|
|
|
if not expense_account:
|
|
raise UserError(_("No expense account found for the product or its category."))
|
|
|
|
moves = self.env['account.move']
|
|
for line in self.line_ids:
|
|
if not line.counterpart_account_id:
|
|
raise UserError(_("Please specify a Counterpart Account for the receipt: %s") % line.description)
|
|
|
|
move_vals = {
|
|
'journal_id': self.journal_id.id,
|
|
'date': self.date,
|
|
'ref': f"Realization: {self.expense_id.name} - {line.description}",
|
|
'move_type': 'entry',
|
|
'line_ids': [
|
|
Command.create({
|
|
'name': f"Realization: {self.expense_id.name} ({line.description})",
|
|
'account_id': expense_account.id,
|
|
'debit': 0.0,
|
|
'credit': line.amount,
|
|
'partner_id': self.employee_id.sudo().work_contact_id.id,
|
|
'expense_id': self.expense_id.id,
|
|
}),
|
|
Command.create({
|
|
'name': f"Realization Counterpart: {line.description}",
|
|
'account_id': line.counterpart_account_id.id,
|
|
'debit': line.amount,
|
|
'credit': 0.0,
|
|
'partner_id': self.employee_id.sudo().work_contact_id.id,
|
|
}),
|
|
],
|
|
}
|
|
move = self.env['account.move'].create(move_vals)
|
|
move.action_post()
|
|
line.move_id = move.id
|
|
moves |= move
|
|
|
|
self.write({
|
|
'state': 'posted',
|
|
'move_id': moves[0].id if moves else False
|
|
})
|
|
|
|
class HrExpenseRealizationLine(models.Model):
|
|
_name = 'hr.expense.realization.line'
|
|
_description = 'Expense Realization Line'
|
|
|
|
realization_id = fields.Many2one('hr.expense.realization', string='Realization', ondelete='cascade', required=True)
|
|
currency_id = fields.Many2one('res.currency', related='realization_id.currency_id')
|
|
description = fields.Char(string='Description', required=True)
|
|
amount = fields.Monetary(string='Amount', required=True, currency_field='currency_id')
|
|
attachment_id = fields.Binary(string='Receipt Attachment')
|
|
attachment_name = fields.Char(string='Attachment Name')
|
|
counterpart_account_id = fields.Many2one('account.account', string='Counterpart Account', groups="account.group_account_invoice")
|
|
move_id = fields.Many2one('account.move', string='Journal Entry', readonly=True, groups="account.group_account_invoice")
|