from odoo import models, api, fields, _
from odoo.exceptions import UserError, ValidationError
from datetime import date, datetime, timedelta
# import datetime
import logging
_logger = logging.getLogger(__name__)
from terbilang import Terbilang
import pytz
from pytz import timezone
class DownPayment(models.Model):
_name = 'down.payment'
_description = 'Down Payment Management'
_rec_name = 'number'
_inherit = ['mail.thread']
user_id = fields.Many2one('res.users', string='Diajukan Oleh', default=lambda self: self.env.user, tracking=3)
partner_id = fields.Many2one('res.partner', string='Partner', related='user_id.partner_id', readonly=True)
number = fields.Char(string='No. Dokumen', default='New Draft', tracking=3)
applicant_name = fields.Char(string='Nama Pemohon', tracking=3, required=True)
nominal = fields.Float(string='Nominal', tracking=3, required=True)
bank_name = fields.Char(string='Bank', tracking=3, required=True)
account_name = fields.Char(string='Nama Account', tracking=3, required=True)
bank_account = fields.Char(string='No. Rekening', tracking=3, required=True)
detail_note = fields.Text(string='Keterangan Penggunaan Rinci', tracking=3)
date_back_to_office = fields.Date(
string='Tanggal Kembali ke Kantor',
tracking=3,
required=True
)
estimated_return_date = fields.Date(
string='Batas Pengajuan',
help='Tanggal batas maksimal pengajuan realisasi setelah kembali ke kantor. '
'7 hari setelah tanggal kembali.'
)
days_remaining = fields.Integer(
string='Sisa Hari Pengajuan',
compute='_compute_days_remaining',
help='Sisa hari batas maksimal pengajuan realisasi setelah kembali ke kantor. '
'7 hari setelah tanggal kembali.'
)
status = fields.Selection([
('draft', 'Draft'),
('pengajuan1', 'Menunggu Approval Departement'),
('pengajuan2', 'Menunggu Pengecekan AP'),
('pengajuan3', 'Menunggu Approval Pimpinan'),
('approved', 'Approved'),
('reject', 'Rejected')
], string='Status', default='draft', tracking=3, index=True, track_visibility='onchange')
last_status = fields.Selection([
('draft', 'Draft'),
('pengajuan1', 'Menunggu Approval Departement'),
('pengajuan2', 'Menunggu Pengecekan AP'),
('pengajuan3', 'Menunggu Approval Pimpinan'),
('approved', 'Approved'),
('reject', 'Rejected')
], string='Status')
status_pay_down_payment = fields.Selection([
('pending', 'Pending'),
('payment', 'Payment'),
], string='Status Pembayaran', default='pending', tracking=3)
name_approval_departement = fields.Char(string='Approval Departement', tracking=True)
name_approval_ap = fields.Char(string='Approval AP', tracking=True)
email_ap = fields.Char(string = 'Email AP')
email_user = fields.Char(string = 'Email User', default=lambda self: self.env.user.email)
name_approval_pimpinan = fields.Char(string='Approval Pimpinan', tracking=True)
date_approved_department = fields.Datetime(string="Date Approved Department")
date_approved_ap = fields.Datetime(string="Date Approved AP")
date_approved_pimpinan = fields.Datetime(string="Date Approved Pimpinan")
position_department = fields.Char(string='Position Departement', tracking=True)
position_ap = fields.Char(string='Position AP', tracking=True)
position_pimpinan = fields.Char(string='Position Pimpinan', tracking=True)
departement_type = fields.Selection([
('sales', 'Sales'),
('merchandiser', 'Merchandiser'),
('marketing', 'Marketing'),
('logistic', 'Logistic'),
('procurement', 'Procurement'),
('fat', 'FAT'),
('hr_ga', 'HR & GA'),
], string='Departement Type', tracking=3, required=True)
attachment_file_image = fields.Binary(string='Attachment Image', attachment_filename='attachment_filename_image')
attachment_file_pdf = fields.Binary(string='Attachment PDF', attachment_filename='attachment_filename_pdf')
attachment_filename_image = fields.Char(string='Filename Image')
attachment_filename_pdf = fields.Char(string='Filename PDF')
attachment_type = fields.Selection([
('pdf', 'PDF'),
('image', 'Image'),
], string="Attachment Type", default='pdf')
move_id = fields.Many2one('account.move', string='Journal Entries', domain=[('move_type', '=', 'entry')])
is_cab_visible = fields.Boolean(string='Is Journal Uang Muka Visible', compute='_compute_is_cab_visible')
reason_reject = fields.Text(string='Alasan Penolakan')
@api.depends('move_id.state')
def _compute_is_cab_visible(self):
for rec in self:
move = rec.move_id
rec.is_cab_visible = bool(move and move.state == 'posted')
def action_view_journal_uangmuka(self):
self.ensure_one()
ap_user_ids = [23, 9468]
if self.env.user.id not in ap_user_ids:
raise UserError('Hanya User AP yang dapat menggunakan fitur ini.')
if not self.move_id:
raise UserError("Journal Uang Muka belum tersedia.")
return {
'name': 'Journal Entry',
'view_mode': 'form',
'res_model': 'account.move',
'type': 'ir.actions.act_window',
'res_id': self.move_id.id,
'target': 'current',
}
@api.onchange('attachment_type')
def _onchange_attachment_type(self):
self.attachment_file_image = False
self.attachment_filename_image = False
self.attachment_file_pdf = False
self.attachment_filename_pdf = False
# Sales & MD : Darren ID 19
# Marketing : Iwan ID 216
# Logistic & Procurement : Rafly H ID 21
# FAT : Stephan ID 28
# HR & GA : Akbar ID 7 / Pimpinan
# ---------------------------------------
# AP : Manzila (Finance) ID 23
def _get_departement_approver(self):
mapping = {
'sales': 19,
'merchandiser': 19,
'marketing': 216,
'logistic': 21,
'procurement': 21,
'fat': 28,
'hr_ga': 7,
}
return mapping.get(self.departement_type)
def action_realisasi_pum(self):
self.ensure_one()
realization = self.env['realization.down.payment'].search([('pum_id', '=', self.id)], limit=1)
if realization:
return {
'type': 'ir.actions.act_window',
'name': 'Realisasi PUM',
'res_model': 'realization.down.payment',
'view_mode': 'form',
'target': 'current',
'res_id': realization.id,
}
else:
return {
'type': 'ir.actions.act_window',
'name': 'Realisasi PUM',
'res_model': 'realization.down.payment',
'view_mode': 'form',
'target': 'current',
'context': {
'default_pum_id': self.id,
'default_value_down_payment': self.nominal,
'default_name': f'Realisasi - {self.number or ""}',
'default_pemberian_line_ids': [
(0, 0, {
'date': self.create_date.date() if self.create_date else fields.Date.today(),
'description': 'Uang Muka',
'value': self.nominal
})
]
}
}
def action_confirm_payment(self):
for rec in self:
if not rec.attachment_file_image and not rec.attachment_file_pdf:
raise UserError('Tidak bisa konfirmasi pembayaran karena belum ada bukti attachment (PDF/Image).')
rec.status_pay_down_payment = 'payment'
# def action_approval_check(self):
# for record in self:
# # user = record.user_id
# user = self.env['res.users'].browse(3401)
# roles = sorted(set(
# f"{group
# .name} (Category: {group.category_id.name})"
# for group in user.groups_id
# if group.category_id.name == 'Roles'
# ))
# _logger.info(f"[ROLE CHECK] User: {user.name} (Login: {user.login}) Roles: {roles}")
# return
def action_approval_check(self):
jakarta_tz = pytz.timezone('Asia/Jakarta')
now = datetime.now(jakarta_tz).replace(tzinfo=None)
formatted_date = now.strftime('%d %B %Y %H:%M')
for rec in self:
if not rec.departement_type:
raise UserError("Field 'departement_type' wajib diisi sebelum approval.")
approver_id = rec._get_departement_approver()
if rec.status == 'pengajuan1':
if self.env.user.id != approver_id:
raise UserError("Hanya approver departement yang berhak menyetujui tahap ini.")
rec.name_approval_departement = self.env.user.name
rec.date_approved_department = now
# Mapping posisi berdasarkan departement_type
department_titles = {
'sales': 'Sales Manager',
'merchandiser': 'Merchandiser Manager',
'marketing': 'Marketing Manager',
'logistic': 'Logistic Manager',
'procurement': 'Procurement Manager',
'fat': 'Finance & Accounting Manager',
'hr_ga': 'HR & GA Manager',
}
rec.position_department = department_titles.get(rec.departement_type, 'Departement Manager')
rec.status = 'pengajuan2'
rec.message_post(
body=f"Approval Departement oleh {self.env.user.name} "
f"pada {formatted_date}."
)
elif rec.status == 'pengajuan2':
ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP
if self.env.user.id not in ap_user_ids:
raise UserError("Hanya AP yang berhak menyetujui tahap ini.")
rec.name_approval_ap = self.env.user.name
rec.email_ap = self.env.user.email
rec.date_approved_ap = now
rec.position_ap = 'Finance AP'
rec.status = 'pengajuan3'
rec.message_post(
body=f"Approval AP oleh {self.env.user.name} "
f"pada {formatted_date}."
)
elif rec.status == 'pengajuan3':
if self.env.user.id != 7: # ID user Pimpinan
raise UserError("Hanya Pimpinan yang berhak menyetujui tahap ini.")
rec.name_approval_pimpinan = self.env.user.name
rec.date_approved_pimpinan = now
rec.position_pimpinan = 'Pimpinan'
rec.status = 'approved'
rec.message_post(
body=f"Approval Pimpinan oleh {self.env.user.name} "
f"pada {formatted_date}."
)
else:
raise UserError("Status saat ini tidak bisa di-approve lagi.")
# rec.message_post(body=f"Approval oleh {self.env.user.name} pada tahap {rec.status}.")
def action_reject(self):
return {
'type': 'ir.actions.act_window',
'name': 'Alasan Penolakan',
'res_model': 'reject.reason.downpayment',
'view_mode': 'form',
'target': 'new',
'context': {'default_request_id': self.id},
}
def action_draft(self):
for record in self:
# Pastikan hanya yang statusnya 'reject' yang bisa di-reset
if record.status != 'reject':
raise UserError("Hanya data dengan status 'Reject' yang bisa dikembalikan ke Draft atau status sebelumnya.")
# Jika ada last_status, gunakan itu; jika tidak, fallback ke 'draft'
new_status = 'pengajuan1'
# Reset field-field approval & alasan reject
record.write({
'status': new_status,
'reason_reject': False,
'last_status': False,
'name_approval_departement': False,
'name_approval_ap': False,
'name_approval_pimpinan': False,
'date_approved_department': False,
'date_approved_ap': False,
'date_approved_pimpinan': False,
'position_department': False,
'position_ap': False,
'position_pimpinan': False,
})
record.message_post(body=f"Status dikembalikan ke {new_status.capitalize()} oleh {self.env.user.name}.")
def action_ap_only(self):
self.ensure_one()
ap_user_ids = [23, 9468] # Ganti sesuai kebutuhan
if self.env.user.id not in ap_user_ids:
raise UserError('Hanya User AP yang dapat menggunakan fitur ini.')
if self.move_id:
raise UserError('CAB / Jurnal sudah pernah dibuat untuk PUM ini.')
return {
'name': 'Create CAB AP Only',
'type': 'ir.actions.act_window',
'res_model': 'down.payment.ap.only',
'view_mode': 'form',
'target': 'new',
'context': {
'default_nominal': self.nominal,
'default_down_payment_id': self.id,
}
}
@api.depends('date_back_to_office')
def _compute_days_remaining(self):
today = date.today()
for rec in self:
if rec.date_back_to_office:
due_date = rec.date_back_to_office + timedelta(days=7)
rec.estimated_return_date = due_date
# Jika hari ini sebelum tanggal kembali, maka anggap belum mulai dihitung
effective_today = max(today, rec.date_back_to_office)
rec.days_remaining = (due_date - effective_today).days
else:
rec.estimated_return_date = False
rec.days_remaining = 0
@api.onchange('date_back_to_office')
def _onchange_date_back_to_office(self):
if self.date_back_to_office and self.date_back_to_office < date.today():
return {
'warning': {
'title': _('Tanggal Tidak Valid'),
'message': _('Tanggal kembali ke kantor tidak boleh lebih awal dari hari ini.')
}
}
@api.onchange('applicant_name')
def _onchange_applicant_name(self):
if self.applicant_name:
self.account_name = self.applicant_name
@api.onchange('account_name')
def _onchange_account_name(self):
if self.account_name:
self.applicant_name = self.account_name
@api.onchange('user_id')
def _onchange_user_id_limit_check(self):
if not self.user_id:
return
pum_ids = self.search([
('user_id', '=', self.user_id.id),
('status', '!=', 'reject')
])
active_pum_count = 0
for pum in pum_ids:
realization = self.env['realization.down.payment'].search([('pum_id', '=', pum.id)], limit=1)
if not realization or realization.done_status != 'done_not_realized':
active_pum_count += 1
if active_pum_count >= 2:
return {
'warning': {
'title': 'Batas Pengajuan Tercapai',
'message': 'User ini sudah memiliki 2 PUM aktif. Tidak dapat mengajukan lagi sampai salah satu direalisasi.',
}
}
@api.model
def create(self, vals):
user = self.env.user
pum_ids = self.search([
('user_id', '=', user.id),
('status', '!=', 'reject')
])
active_pum_count = 0
for pum in pum_ids:
realization = self.env['realization.down.payment'].search([('pum_id', '=', pum.id)], limit=1)
if not realization or realization.done_status != 'done_not_realized':
active_pum_count += 1
if active_pum_count >= 2:
raise UserError("Anda hanya dapat mengajukan maksimal 2 PUM aktif. Silakan realisasikan salah satunya terlebih dahulu.")
if not vals.get('number') or vals['number'] == 'New Draft':
vals['number'] = self.env['ir.sequence'].next_by_code('down.payment') or 'New Draft'
vals['status'] = 'pengajuan1'
return super(DownPayment, self).create(vals)
class RealizationDownPaymentLine(models.Model):
_name = 'realization.down.payment.line'
_description = 'Rincian Pemberian PUM'
realization_id = fields.Many2one('realization.down.payment', string='Realization')
date = fields.Date(string='Tanggal', required=True, default=fields.Date.today)
description = fields.Char(string='Description', required=True)
value = fields.Float(string='Nilai', required=True)
class RealizationDownPaymentUseLine(models.Model):
_name = 'realization.down.payment.use.line'
_description = 'Rincian Penggunaan PUM'
realization_id = fields.Many2one('realization.down.payment', string='Realization')
date = fields.Date(string='Tanggal', required=True, default=fields.Date.today)
description = fields.Char(string='Description', required=True)
nominal = fields.Float(string='Nominal', required=True)
done_attachment = fields.Boolean(string='Checked', default=False)
attachment_type = fields.Selection([
('pdf', 'PDF'),
('image', 'Image'),
], string="Attachment Type", default='pdf')
attachment_file_image = fields.Binary(string='Attachment Image', attachment_filename='attachment_filename_image')
attachment_file_pdf = fields.Binary(string='Attachment PDF', attachment_filename='attachment_filename_pdf')
attachment_filename_image = fields.Char(string='Filename Image')
attachment_filename_pdf = fields.Char(string='Filename PDF')
account_id = fields.Many2one(
'account.account', string='Jenis Biaya', required=True,
domain="[('id', 'in', [484, 486, 488, 506, 507, 625, 471, 519, 527, 528, 529, 530, 565])]" # ID Jenis Biaya yang dibutuhkan
)
@api.onchange('account_id')
def _onchange_account_id(self):
for rec in self:
if rec.account_id:
rec.description = rec.account_id.name + " - "
@api.onchange('attachment_type')
def _onchange_attachment_type(self):
self.attachment_file_image = False
self.attachment_filename_image = False
self.attachment_file_pdf = False
self.attachment_filename_pdf = False
@api.onchange('done_attachment')
def _onchange_done_attachment(self):
ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP
if self.done_attachment and self.env.user.id not in ap_user_ids:
self.done_attachment = False
return {
'warning': {
'title': _('Tidak Diizinkan'),
'message': _('Hanya user AP yang bisa mencentang Done Attachment.')
}
}
class RealizationDownPayment(models.Model):
_name = 'realization.down.payment'
_description = 'Realization Down Payment Management'
_inherit = ['mail.thread']
pum_id = fields.Many2one('down.payment', string='No PUM')
name = fields.Char(string='Nama', readonly=True, tracking=3)
title = fields.Char(string='Judul', tracking=3)
goals = fields.Text(string='Tujuan', tracking=3)
related = fields.Char(string='Terkait', tracking=3,)
pemberian_line_ids = fields.One2many(
'realization.down.payment.line', 'realization_id', string='Rincian Pemberian'
)
penggunaan_line_ids = fields.One2many(
'realization.down.payment.use.line', 'realization_id', string='Rincian Penggunaan'
)
grand_total = fields.Float(string='Grand Total Pemberian', tracking=3, compute='_compute_grand_total')
grand_total_use = fields.Float(string='Grand Total Penggunaan', tracking=3, compute='_compute_grand_total_use')
value_down_payment = fields.Float(string='PUM', tracking=3)
remaining_value = fields.Float(string='Sisa Uang PUM', tracking=3, compute='_compute_remaining_value')
note_approval = fields.Text(string='Note Persetujuan', tracking=3)
name_approval_departement = fields.Char(string='Approval Departement', tracking=True)
name_approval_ap = fields.Char(string='Approval AP', tracking=True)
name_approval_pimpinan = fields.Char(string='Approval Pimpinan', tracking=True)
date_approved_department = fields.Datetime(string="Date Approved Department")
date_approved_ap = fields.Datetime(string="Date Approved AP")
date_approved_pimpinan = fields.Datetime(string="Date Approved Pimpinan")
position_department = fields.Char(string='Position Departement', tracking=True)
position_ap = fields.Char(string='Position AP', tracking=True)
position_pimpinan = fields.Char(string='Position Pimpinan', tracking=True)
status = fields.Selection([
('pengajuan1', 'Menunggu Approval Departement'),
('pengajuan2', 'Menunggu Pengecekan AP'),
('pengajuan3', 'Menunggu Approval Pimpinan'),
('approved', 'Approved'),
], string='Status', default='pengajuan1', tracking=3, index=True, track_visibility='onchange')
done_status = fields.Selection([
('remaining', 'Remaining'),
('done_not_realized', 'Done Not Realized'),
('done_realized', 'Done Realized')
], string='Status Realisasi', tracking=3, default='remaining')
date_done_not_realized = fields.Date(string='Tanggal Done Not Realized', tracking=3)
currency_id = fields.Many2one(
'res.currency', string='Currency',
default=lambda self: self.env.company.currency_id
)
attachment_file_image = fields.Binary(string='Attachment Image', attachment_filename='attachment_filename_image')
attachment_file_pdf = fields.Binary(string='Attachment PDF', attachment_filename='attachment_filename_pdf')
attachment_filename_image = fields.Char(string='Filename Image')
attachment_filename_pdf = fields.Char(string='Filename PDF')
attachment_type = fields.Selection([
('pdf', 'PDF'),
('image', 'Image'),
], string="Attachment Type", default='pdf')
move_id = fields.Many2one('account.move', string='Journal Entries', domain=[('move_type', '=', 'entry')])
is_cab_visible = fields.Boolean(string='Is Journal Uang Muka Visible', compute='_compute_is_cab_visible')
@api.depends('move_id.state')
def _compute_is_cab_visible(self):
for rec in self:
move = rec.move_id
rec.is_cab_visible = bool(move and move.state == 'posted')
def action_view_journal_uangmuka(self):
self.ensure_one()
ap_user_ids = [23, 9468]
if self.env.user.id not in ap_user_ids:
raise UserError('Hanya User AP yang dapat menggunakan fitur ini.')
if not self.move_id:
raise UserError("Journal Uang Muka belum tersedia.")
return {
'name': 'Journal Entry',
'view_mode': 'form',
'res_model': 'account.move',
'type': 'ir.actions.act_window',
'res_id': self.move_id.id,
'target': 'current',
}
@api.onchange('attachment_type')
def _onchange_attachment_type(self):
self.attachment_file_image = False
self.attachment_filename_image = False
self.attachment_file_pdf = False
self.attachment_filename_pdf = False
@api.depends('pemberian_line_ids.value')
def _compute_grand_total(self):
for rec in self:
rec.grand_total = sum(line.value for line in rec.pemberian_line_ids)
@api.depends('penggunaan_line_ids.nominal')
def _compute_grand_total_use(self):
for rec in self:
rec.grand_total_use = sum(line.nominal for line in rec.penggunaan_line_ids)
@api.depends('grand_total', 'grand_total_use')
def _compute_remaining_value(self):
for rec in self:
rec.remaining_value = rec.value_down_payment - rec.grand_total_use
def action_validation(self):
self.ensure_one()
# Validasi hanya AP yang bisa validasi
ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP
if self.env.user.id not in ap_user_ids:
raise UserError('Hanya AP yang dapat melakukan validasi realisasi.')
if self.done_status == 'remaining':
self.done_status = 'done_not_realized'
self.date_done_not_realized = fields.Date.today()
elif self.done_status == 'done_not_realized':
self.done_status = 'done_realized'
else:
raise UserError('Realisasi sudah berstatus Done Realized.')
# Opsional: Tambah log di chatter
self.message_post(body=f"Status realisasi diperbarui menjadi {dict(self._fields['done_status'].selection).get(self.done_status)} oleh {self.env.user.name}.")
def action_cab(self):
self.ensure_one()
ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP
if self.env.user.id not in ap_user_ids:
raise UserError('Hanya User AP yang dapat menggunakan ini.')
if self.move_id:
raise UserError("CAB / Jurnal sudah pernah dibuat untuk Realisasi ini.")
if not self.pum_id or not self.pum_id.move_id:
raise UserError("PUM terkait atau CAB belum tersedia.")
partner_id = self.pum_id.user_id.partner_id.id
cab_move = self.pum_id.move_id
# Account Bank Intransit dari CAB:
bank_intransit_line = cab_move.line_ids.filtered(lambda l: l.account_id.id in [573, 389, 392])
if not bank_intransit_line:
raise UserError("Account Bank Intransit dengan tidak ditemukan di CAB terkait.")
account_sisa_pum = bank_intransit_line[0].account_id.id
# Account Uang Muka Operasional
account_uang_muka = 403
# Tanggal pakai create_date atau hari ini
account_date = self.date_done_not_realized
ref_label = f"Realisasi {self.pum_id.number} Biaya {self.pum_id.detail_note} ({cab_move.name})"
label_sisa_pum = f"Sisa PUM {self.pum_id.detail_note} {self.pum_id.number} ({cab_move.name})"
lines = []
# Sisa PUM (Debit)
if self.remaining_value > 0:
lines.append((0, 0, {
'account_id': account_sisa_pum,
'partner_id': partner_id,
'name': label_sisa_pum,
'debit': self.remaining_value,
'credit': 0,
}))
# Biaya Penggunaan (Debit)
total_biaya = 0
for line in self.penggunaan_line_ids:
lines.append((0, 0, {
'account_id': line.account_id.id,
'partner_id': partner_id,
'name': f"{line.description} ({line.date})",
'debit': line.nominal,
'credit': 0,
}))
total_biaya += line.nominal
# Uang Muka Operasional (Credit)
total_credit = self.remaining_value + total_biaya
if total_credit > 0:
lines.append((0, 0, {
'account_id': account_uang_muka,
'partner_id': partner_id,
'name': ref_label,
'debit': 0,
'credit': total_credit,
}))
move = self.env['account.move'].create({
'ref': ref_label,
'date': account_date,
'journal_id': 11, # MISC
'line_ids': lines,
})
# self.message_post(body=f"Jurnal CAB telah dibuat dengan nomor: {move.name}.")
self.move_id = move.id
return {
'name': _('Journal Entry'),
'view_mode': 'form',
'res_model': 'account.move',
'type': 'ir.actions.act_window',
'res_id': move.id,
'target': 'current',
}
def action_approval_check(self):
jakarta_tz = pytz.timezone('Asia/Jakarta')
now = datetime.now(jakarta_tz).replace(tzinfo=None)
formatted_date = now.strftime('%d %B %Y %H:%M')
for rec in self:
if not rec.pum_id.departement_type:
raise UserError("Field 'departement_type' wajib diisi sebelum approval.")
approver_id = rec.pum_id._get_departement_approver()
if rec.status == 'pengajuan1':
if self.env.user.id != approver_id:
raise UserError("Hanya approver departement yang berhak menyetujui tahap ini.")
rec.name_approval_departement = self.env.user.name
rec.date_approved_department = now
# Mapping posisi berdasarkan departement_type
department_titles = {
'sales': 'Sales Manager',
'merchandiser': 'Merchandiser Manager',
'marketing': 'Marketing Manager',
'logistic': 'Logistic Manager',
'procurement': 'Procurement Manager',
'fat': 'Finance & Accounting Manager',
'hr_ga': 'HR & GA Manager',
}
rec.position_department = department_titles.get(rec.pum_id.departement_type, 'Departement Manager')
rec.status = 'pengajuan2'
rec.message_post(
body=f"Approval Departement oleh {self.env.user.name} "
f"pada {formatted_date}."
)
elif rec.status == 'pengajuan2':
ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP
if self.env.user.id not in ap_user_ids:
raise UserError("Hanya AP yang berhak menyetujui tahap ini.")
rec.name_approval_ap = self.env.user.name
rec.date_approved_ap = now
rec.position_ap = 'Finance AP'
rec.status = 'pengajuan3'
rec.message_post(
body=f"Approval AP oleh {self.env.user.name} "
f"pada {formatted_date}."
)
elif rec.status == 'pengajuan3':
if self.env.user.id != 7: # ID user Pimpinan
raise UserError("Hanya Pimpinan yang berhak menyetujui tahap ini.")
rec.name_approval_pimpinan = self.env.user.name
rec.date_approved_pimpinan = now
rec.position_pimpinan = 'Pimpinan'
rec.status = 'approved'
rec.done_status = 'done_not_realized' # Set status done untuk realisasi
rec.message_post(
body=f"Approval Pimpinan oleh {self.env.user.name} "
f"pada {formatted_date}."
)
else:
raise UserError("Status saat ini tidak bisa di-approve lagi.")
# rec.message_post(body=f"Approval oleh {self.env.user.name} pada tahap {rec.status}.")
class RejectReasonDownPayment(models.TransientModel):
_name = 'reject.reason.downpayment'
_description = 'Wizard for Reject Reason Down Payment'
request_id = fields.Many2one('down.payment', string='Pengajuan PUM')
reason_reject = fields.Text(string='Alasan Penolakan', required=True)
def confirm_reject(self):
if self.request_id:
self.request_id.write({
'status': 'reject',
'last_status': self.request_id.status,
'reason_reject': self.reason_reject,
})
return {'type': 'ir.actions.act_window_close'}
class DownPaymentApOnly(models.TransientModel):
_name = 'down.payment.ap.only'
_description = 'Create CAB from Down Payment for AP Only'
down_payment_id = fields.Many2one('down.payment', string='Down Payment', required=True)
account_id = fields.Many2one(
'account.account', string='Bank Intransit', required=True,
domain="[('id', 'in', [573, 389, 392])]" # ID Bank Intransit
)
nominal = fields.Float(string='Nominal', related='down_payment_id.nominal')
def action_create_cab(self):
self.ensure_one()
# if self.env.user.id != 23:
# raise UserError('Hanya AP yang dapat menggunakan ini.')
dp = self.down_payment_id
partner_id = dp.user_id.partner_id.id
ref_label = f'{dp.number} - Biaya {dp.detail_note or "-"}'
move = self.env['account.move'].create({
'ref': ref_label,
'date': fields.Date.context_today(self),
'journal_id': 11, # Cash & Bank
'line_ids': [
(0, 0, {
'account_id': 403, # Uang Muka Operasional
'partner_id': partner_id,
'name': ref_label,
'debit': dp.nominal,
'credit': 0,
}),
(0, 0, {
'account_id': self.account_id.id, # Bank Intransit yang dipilih
'partner_id': partner_id,
'name': ref_label,
'debit': 0,
'credit': dp.nominal,
})
]
})
dp.move_id = move.id # jika ada field untuk menampung move_id
return {
'name': _('Journal Entry'),
'view_mode': 'form',
'res_model': 'account.move',
'type': 'ir.actions.act_window',
'res_id': move.id,
'target': 'current',
}