From 861ceb85ad813050331200b41ba4fe08b314d08f Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 16 Jul 2025 14:55:03 +0700 Subject: (andri) init pum --- indoteknik_custom/models/down_payment.py | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 indoteknik_custom/models/down_payment.py (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py new file mode 100644 index 00000000..8b57bc2f --- /dev/null +++ b/indoteknik_custom/models/down_payment.py @@ -0,0 +1,97 @@ +from odoo import models, api, fields, _ +from odoo.exceptions import UserError +from datetime import datetime +# import datetime +import logging +from terbilang import Terbilang +import pytz + + +class DownPayment(models.Model): + _name = 'down.payment' + _description = 'Down Payment Management' + + number = fields.Char(string='No. Dokumen', required=True, 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) + + status = fields.Selection([ + ('draft', 'Draft'), + ('pengajuan1', 'Approval Departement'), + ('pengajuan2', 'Approval AP'), + ('pengajuan3', 'Approval Pimpinan'), + ('approved', 'Approved'), + ('reject', 'Rejected') + ], string='Status Down Payment', default='draft', tracking=3) + + status_pay_down_payment = fields.Selection([ + ('pending', 'Pending'), + ('payment', 'Payment'), + ], string='Status Pembayaran', default='pending', tracking=3) + + departement_type = fields.Selection([ + ('sales', 'Sales'), + ('md', 'MD'), + ('marketing', 'Marketing'), + ('logistic', 'Logistic'), + ('procurement', 'Procurement'), + ('fat', 'FAT'), + ('hr_ga', 'HR & GA'), + ], string='Departement Type', tracking=3, required=True) + + # 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 + + created_date = fields.Datetime(string='Created Date', default=lambda self: fields.Datetime.now(), tracking=3) + + +class RealizationDownPayment(models.Model): + _name = 'realization.down.payment' + _description = 'Realization Down Payment Management' + + title = fields.Char(string='Judul', required=True, tracking=3) + goals = fields.Text(string='Tujuan', tracking=3, required=True) + related = fields.Char(string='Terkait', tracking=3, required=True) + + # Rincian Pemberian PUM + date_line = fields.Date(string='Tanggal', required=True, tracking=3, default=lambda self: fields.Date.today()) + info_line = fields.Char(string='Description', required=True, tracking=3) + value_line = fields.Float(string='Nilai', required=True, tracking=3) + grand_total = fields.Float(string='Grand Total', tracking=3) + + # Rincian Penggunaan PUM + date_line_use = fields.Date(string='Tanggal', required=True, tracking=3, default=lambda self: fields.Date.today()) + info_line_use = fields.Char(string='Description', required=True, tracking=3) + value_line_use = fields.Float(string='Nominal', required=True, tracking=3) + attachment = fields.Boolean(string='Attachment', tracking=3, default=False) + grand_total_use = fields.Float(string='Grand Total', tracking=3) + value_down_payment = fields.Float(string='PUM', required=True, tracking=3) + remaining_value = fields.Float(string='Sisa Uang PUM', tracking=3) + + note_approval = fields.Text(string='Note Persetujuan', tracking=3) + + + + + + + + + + + + + + + -- cgit v1.2.3 From 2fe077d802bf143b110bc681ac039ba8bcfccb26 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 17 Jul 2025 13:57:47 +0700 Subject: (andri) init action dll --- indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/down_payment.py | 49 ++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 9 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index b815b472..1c54efc6 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -153,3 +153,4 @@ from . import sale_order_delay from . import approval_invoice_date from . import approval_payment_term # from . import patch +from . import down_payment diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 8b57bc2f..7a3a39f1 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -5,13 +5,17 @@ from datetime import datetime import logging from terbilang import Terbilang import pytz +from pytz import timezone class DownPayment(models.Model): _name = 'down.payment' _description = 'Down Payment Management' + _rec_name = 'number' - number = fields.Char(string='No. Dokumen', required=True, tracking=3) + user_id = fields.Many2one('res.users', string='Diajukan Oleh', default=lambda self: self.env.user, tracking=3) + + 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) @@ -23,12 +27,12 @@ class DownPayment(models.Model): status = fields.Selection([ ('draft', 'Draft'), - ('pengajuan1', 'Approval Departement'), - ('pengajuan2', 'Approval AP'), - ('pengajuan3', 'Approval Pimpinan'), + ('pengajuan1', 'Menunggu Approval Departement'), + ('pengajuan2', 'Menunggu Pengecekan AP'), + ('pengajuan3', 'Menunggu Approval Pimpinan'), ('approved', 'Approved'), ('reject', 'Rejected') - ], string='Status Down Payment', default='draft', tracking=3) + ], string='Status', default='draft', tracking=3, index=True, track_visibility='onchange') status_pay_down_payment = fields.Selection([ ('pending', 'Pending'), @@ -37,7 +41,7 @@ class DownPayment(models.Model): departement_type = fields.Selection([ ('sales', 'Sales'), - ('md', 'MD'), + ('merchandiser', 'Merchandiser'), ('marketing', 'Marketing'), ('logistic', 'Logistic'), ('procurement', 'Procurement'), @@ -53,24 +57,50 @@ class DownPayment(models.Model): # --------------------------------------- # AP : Manzila (Finance) ID 23 - created_date = fields.Datetime(string='Created Date', default=lambda self: fields.Datetime.now(), tracking=3) + def action_realisasi_pum(self): + self.ensure_one() + # Logic untuk realisasi PUM + return + + def action_confirm_payment(self): + self.ensure_one() + # Logic untuk konfirmasi pembayaran + return + + def action_reject(self): + self.ensure_one() + # Logic untuk konfirmasi pembayaran + return + + def action_approval_check(self): + self.ensure_one() + # Logic untuk konfirmasi pembayaran + return + + @api.model + def create(self, vals): + if not vals.get('number') or vals['number'] == 'New Draft': + vals['number'] = self.env['ir.sequence'].next_by_code('down.payment') or 'New Draft' + return super(DownPayment, self).create(vals) + class RealizationDownPayment(models.Model): _name = 'realization.down.payment' _description = 'Realization Down Payment Management' + # number = fields.Char(string='No. Dokumen', tracking=3) title = fields.Char(string='Judul', required=True, tracking=3) goals = fields.Text(string='Tujuan', tracking=3, required=True) related = fields.Char(string='Terkait', tracking=3, required=True) - # Rincian Pemberian PUM + # Page Rincian Pemberian PUM date_line = fields.Date(string='Tanggal', required=True, tracking=3, default=lambda self: fields.Date.today()) info_line = fields.Char(string='Description', required=True, tracking=3) value_line = fields.Float(string='Nilai', required=True, tracking=3) grand_total = fields.Float(string='Grand Total', tracking=3) - # Rincian Penggunaan PUM + # Page Rincian Penggunaan PUM date_line_use = fields.Date(string='Tanggal', required=True, tracking=3, default=lambda self: fields.Date.today()) info_line_use = fields.Char(string='Description', required=True, tracking=3) value_line_use = fields.Float(string='Nominal', required=True, tracking=3) @@ -88,6 +118,7 @@ class RealizationDownPayment(models.Model): + -- cgit v1.2.3 From b839d55d39856031b6abfeb8f304b77b0fb145ce Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 18 Jul 2025 11:35:02 +0700 Subject: (andri) add chatter & make realization form --- indoteknik_custom/models/down_payment.py | 138 ++++++++++++++++++++++++------- 1 file changed, 109 insertions(+), 29 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 7a3a39f1..14f0b171 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -3,6 +3,7 @@ from odoo.exceptions import UserError from datetime import datetime # import datetime import logging +_logger = logging.getLogger(__name__) from terbilang import Terbilang import pytz from pytz import timezone @@ -12,6 +13,7 @@ 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) @@ -34,6 +36,15 @@ class DownPayment(models.Model): ('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'), @@ -57,25 +68,56 @@ class DownPayment(models.Model): # --------------------------------------- # 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() - # Logic untuk realisasi PUM - return + 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, + } + } + def action_confirm_payment(self): - self.ensure_one() # Logic untuk konfirmasi pembayaran return - def action_reject(self): - self.ensure_one() - # Logic untuk konfirmasi pembayaran + 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): - self.ensure_one() + def action_reject(self): # Logic untuk konfirmasi pembayaran - return + return + + def action_draft(self): + for record in self: + record.status = record.last_status if record.last_status else 'draft' + return @api.model def create(self, vals): @@ -84,33 +126,71 @@ class DownPayment(models.Model): 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) + attachment = fields.Boolean(string='Lampiran', default=False) + class RealizationDownPayment(models.Model): _name = 'realization.down.payment' _description = 'Realization Down Payment Management' + _inherit = ['mail.thread'] - # number = fields.Char(string='No. Dokumen', tracking=3) - title = fields.Char(string='Judul', required=True, tracking=3) - goals = fields.Text(string='Tujuan', tracking=3, required=True) - related = fields.Char(string='Terkait', tracking=3, required=True) - - # Page Rincian Pemberian PUM - date_line = fields.Date(string='Tanggal', required=True, tracking=3, default=lambda self: fields.Date.today()) - info_line = fields.Char(string='Description', required=True, tracking=3) - value_line = fields.Float(string='Nilai', required=True, tracking=3) - grand_total = fields.Float(string='Grand Total', tracking=3) - - # Page Rincian Penggunaan PUM - date_line_use = fields.Date(string='Tanggal', required=True, tracking=3, default=lambda self: fields.Date.today()) - info_line_use = fields.Char(string='Description', required=True, tracking=3) - value_line_use = fields.Float(string='Nominal', required=True, tracking=3) - attachment = fields.Boolean(string='Attachment', tracking=3, default=False) - grand_total_use = fields.Float(string='Grand Total', tracking=3) - value_down_payment = fields.Float(string='PUM', required=True, tracking=3) - remaining_value = fields.Float(string='Sisa Uang PUM', tracking=3) + pum_id = fields.Many2one('down.payment', string='No PUM') + 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) + currency_id = fields.Many2one( + 'res.currency', string='Currency', + default=lambda self: self.env.company.currency_id + ) + + @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 + -- cgit v1.2.3 From 51a363741a7c40d91052e90f7d1aa0f6defc2f99 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 18 Jul 2025 14:30:09 +0700 Subject: (andri) add due date --- indoteknik_custom/models/down_payment.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 14f0b171..e9fd51d0 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -1,6 +1,6 @@ from odoo import models, api, fields, _ from odoo.exceptions import UserError -from datetime import datetime +from datetime import date, datetime, timedelta # import datetime import logging _logger = logging.getLogger(__name__) @@ -27,6 +27,15 @@ class DownPayment(models.Model): bank_account = fields.Char(string='No. Rekening', tracking=3, required=True) detail_note = fields.Text(string='Keterangan Penggunaan Rinci', tracking=3) + estimated_return_date = fields.Date( + string='Estimasi Batas Durasi Pengajuan' + ) + + days_remaining = fields.Integer( + string='Due Date', + compute='_compute_days_remaining', + ) + status = fields.Selection([ ('draft', 'Draft'), ('pengajuan1', 'Menunggu Approval Departement'), @@ -117,12 +126,25 @@ class DownPayment(models.Model): def action_draft(self): for record in self: record.status = record.last_status if record.last_status else 'draft' - return + return + + @api.depends('create_date') + def _compute_days_remaining(self): + today = date.today() + for rec in self: + if rec.create_date: + due_date = rec.create_date.date() + timedelta(days=7) + rec.days_remaining = (due_date - today).days + else: + rec.days_remaining = 0 @api.model def create(self, vals): if not vals.get('number') or vals['number'] == 'New Draft': vals['number'] = self.env['ir.sequence'].next_by_code('down.payment') or 'New Draft' + + if not vals.get('estimated_return_date'): + vals['estimated_return_date'] = date.today() + timedelta(days=7) return super(DownPayment, self).create(vals) @@ -191,6 +213,9 @@ class RealizationDownPayment(models.Model): for rec in self: rec.remaining_value = rec.value_down_payment - rec.grand_total_use + def action_validation(self): + # Logic untuk konfirmasi pembayaran + return -- cgit v1.2.3 From 17c147f8c988c36c46e035e954c0d90f3ea18f20 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 19 Jul 2025 08:51:19 +0700 Subject: (andri) fix realization --- indoteknik_custom/models/down_payment.py | 46 +++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 10 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index e9fd51d0..68f6954d 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -69,6 +69,9 @@ class DownPayment(models.Model): ('hr_ga', 'HR & GA'), ], string='Departement Type', tracking=3, required=True) + attachment_file = fields.Binary(string="Attachment") + + # Sales & MD : Darren ID 19 # Marketing : Iwan ID 216 # Logistic & Procurement : Rafly H ID 21 @@ -90,17 +93,39 @@ class DownPayment(models.Model): return mapping.get(self.departement_type) def action_realisasi_pum(self): - 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, + 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): @@ -175,6 +200,7 @@ class RealizationDownPayment(models.Model): _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,) -- cgit v1.2.3 From f9cd84314e7a725fbe5001aa39201f632562bfc6 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 19 Jul 2025 11:15:15 +0700 Subject: (andri) add attachment & add status done validation at realization --- indoteknik_custom/models/down_payment.py | 41 ++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 68f6954d..ad359408 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -69,8 +69,22 @@ class DownPayment(models.Model): ('hr_ga', 'HR & GA'), ], string='Departement Type', tracking=3, required=True) - attachment_file = fields.Binary(string="Attachment") - + 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') + + @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 @@ -219,6 +233,12 @@ class RealizationDownPayment(models.Model): note_approval = fields.Text(string='Note Persetujuan', tracking=3) + done_status = fields.Selection([ + ('remaining', 'Remaining'), + ('done_not_realized', 'Done Not Realized'), + ('done_realized', 'Done Realized') + ], string='Status Realisasi', tracking=3, default='remaining') + currency_id = fields.Many2one( 'res.currency', string='Currency', default=lambda self: self.env.company.currency_id @@ -240,8 +260,21 @@ class RealizationDownPayment(models.Model): rec.remaining_value = rec.value_down_payment - rec.grand_total_use def action_validation(self): - # Logic untuk konfirmasi pembayaran - return + self.ensure_one() + + # Validasi hanya AP yang bisa validasi + if self.env.user.id != 23: + raise UserError('Hanya AP yang dapat melakukan validasi realisasi.') + + if self.done_status == 'remaining': + self.done_status = 'done_not_realized' + 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}.") -- cgit v1.2.3 From 09c24278d60eb5bc5a92ad9b3f5389d329641228 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 19 Jul 2025 13:47:28 +0700 Subject: (andri) add attachment & validasi realisasi penggunaan PUM --- indoteknik_custom/models/down_payment.py | 51 +++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index ad359408..920b1b5b 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -205,8 +205,40 @@ class RealizationDownPaymentUseLine(models.Model): 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) - attachment = fields.Boolean(string='Lampiran', default=False) + 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') + + @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): + if self.done_attachment and self.env.user.id != 23: + self.done_attachment = False + return { + 'warning': { + 'title': _('Tidak Diizinkan'), + 'message': _('Hanya user AP yang bisa mencentang Done Attachment.') + } + } + # def write(self, vals): + # if 'done_attachment' in vals and vals['done_attachment']: + # if self.env.user.id != 23: # Manzila (Finance) ID 23 + # raise UserError('Hanya user AP yang dapat mencentang Checked.') + # return super().write(vals) class RealizationDownPayment(models.Model): _name = 'realization.down.payment' @@ -244,6 +276,23 @@ class RealizationDownPayment(models.Model): 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') + + @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: -- cgit v1.2.3 From 698e075cf3874d4eaf5ac3f44f4a5b1b1832b93c Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 19 Jul 2025 15:11:31 +0700 Subject: (andri) add widget for remaining & due date, add validasi confirm payment --- indoteknik_custom/models/down_payment.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 920b1b5b..df6c0eba 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -143,8 +143,12 @@ class DownPayment(models.Model): def action_confirm_payment(self): - # Logic untuk konfirmasi pembayaran - return + 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: -- cgit v1.2.3 From b265e24136b722ab44b52b667dcd19eba3878aa7 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 19 Jul 2025 15:12:33 +0700 Subject: (andri) delete --- indoteknik_custom/models/down_payment.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index df6c0eba..0f39c98a 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -238,11 +238,6 @@ class RealizationDownPaymentUseLine(models.Model): 'message': _('Hanya user AP yang bisa mencentang Done Attachment.') } } - # def write(self, vals): - # if 'done_attachment' in vals and vals['done_attachment']: - # if self.env.user.id != 23: # Manzila (Finance) ID 23 - # raise UserError('Hanya user AP yang dapat mencentang Checked.') - # return super().write(vals) class RealizationDownPayment(models.Model): _name = 'realization.down.payment' -- cgit v1.2.3 From 6b68c39ebe116ccb16a5d77ab74b8b4a59958f20 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 21 Jul 2025 14:26:24 +0700 Subject: (andri) add wizard CAB di PUM dan hanya AP yang bisa akses --- indoteknik_custom/models/down_payment.py | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 0f39c98a..881829c8 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -16,6 +16,7 @@ class DownPayment(models.Model): _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) @@ -79,6 +80,29 @@ class DownPayment(models.Model): ('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() + 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 @@ -171,6 +195,22 @@ class DownPayment(models.Model): record.status = record.last_status if record.last_status else 'draft' return + def action_ap_only(self): + # if self.env.user.id != 23: + # raise UserError('Hanya AP yang dapat menggunakan 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('create_date') def _compute_days_remaining(self): today = date.today() @@ -325,6 +365,63 @@ class RealizationDownPayment(models.Model): self.message_post(body=f"Status realisasi diperbarui menjadi {dict(self._fields['done_status'].selection).get(self.done_status)} oleh {self.env.user.name}.") +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="[('name', 'ilike', 'intransit')]" + ) + nominal = fields.Float(string='Nominal', related='down_payment_id.nominal', readonly=True) + + 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', + } + + + -- cgit v1.2.3 From 96b986a27a356dfd026144ceed41608adde661a1 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 22 Jul 2025 13:08:25 +0700 Subject: (andri) add validation approval, add jenis biaya di line penggunaan realisasi --- indoteknik_custom/models/down_payment.py | 73 ++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 14 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 881829c8..573028b9 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -60,6 +60,10 @@ class DownPayment(models.Model): ('payment', 'Payment'), ], string='Status Pembayaran', default='pending', tracking=3) + name_approval_departement = fields.Char(string='Approval Departement') + name_approval_ap = fields.Char(string='Approval AP') + name_approval_pimpinan = fields.Char(string='Approval Pimpinan') + departement_type = fields.Selection([ ('sales', 'Sales'), ('merchandiser', 'Merchandiser'), @@ -174,17 +178,45 @@ class DownPayment(models.Model): 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): - 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 + for rec in self: + 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.status = 'pengajuan2' + + elif rec.status == 'pengajuan2': + if self.env.user.id != 23: # ID user AP + raise UserError("Hanya AP yang berhak menyetujui tahap ini.") + rec.name_approval_ap = self.env.user.name + rec.status = 'pengajuan3' + + 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.status = 'approved' + + 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): # Logic untuk konfirmasi pembayaran @@ -197,7 +229,7 @@ class DownPayment(models.Model): def action_ap_only(self): # if self.env.user.id != 23: - # raise UserError('Hanya AP yang dapat menggunakan ini.') + # raise UserError('Hanya User AP yang dapat menggunakan ini.') return { 'name': 'Create CAB AP Only', 'type': 'ir.actions.act_window', @@ -225,9 +257,9 @@ class DownPayment(models.Model): def create(self, vals): if not vals.get('number') or vals['number'] == 'New Draft': vals['number'] = self.env['ir.sequence'].next_by_code('down.payment') or 'New Draft' - if not vals.get('estimated_return_date'): vals['estimated_return_date'] = date.today() + timedelta(days=7) + vals['status'] = 'pengajuan1' return super(DownPayment, self).create(vals) @@ -261,6 +293,17 @@ class RealizationDownPaymentUseLine(models.Model): 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 @@ -364,6 +407,8 @@ class RealizationDownPayment(models.Model): # 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_misc(self): + return class DownPaymentApOnly(models.TransientModel): _name = 'down.payment.ap.only' @@ -372,9 +417,9 @@ class DownPaymentApOnly(models.TransientModel): down_payment_id = fields.Many2one('down.payment', string='Down Payment', required=True) account_id = fields.Many2one( 'account.account', string='Bank Intransit', required=True, - domain="[('name', 'ilike', 'intransit')]" + domain="[('id', 'in', [573, 389, 392])]" # ID Bank Intransit ) - nominal = fields.Float(string='Nominal', related='down_payment_id.nominal', readonly=True) + nominal = fields.Float(string='Nominal', related='down_payment_id.nominal') def action_create_cab(self): self.ensure_one() -- cgit v1.2.3 From fbb10e5244efae7e459d26b92c81c5eda07b93ea Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 22 Jul 2025 16:57:06 +0700 Subject: (andri) init status pada realisasi dan menambahkan action cab only AP --- indoteknik_custom/models/down_payment.py | 123 ++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 573028b9..e43d2a0c 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -347,6 +347,24 @@ class RealizationDownPayment(models.Model): note_approval = fields.Text(string='Note Persetujuan', tracking=3) + 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') + done_status = fields.Selection([ ('remaining', 'Remaining'), ('done_not_realized', 'Done Not Realized'), @@ -368,6 +386,30 @@ class RealizationDownPayment(models.Model): ('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() + 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 @@ -407,8 +449,85 @@ class RealizationDownPayment(models.Model): # 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_misc(self): - return + def action_cab(self): + self.ensure_one() + + 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.create_date.date() if self.create_date else fields.Date.today() + + 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', + } class DownPaymentApOnly(models.TransientModel): _name = 'down.payment.ap.only' -- cgit v1.2.3 From 7e6927b4fd7bcd9981ce56f3fc24f8a1b685be0d Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 23 Jul 2025 09:40:56 +0700 Subject: (andri) add validasi urutan approval pengajuan & realisasi PUM --- indoteknik_custom/models/down_payment.py | 138 ++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index e43d2a0c..afb8ca73 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -60,9 +60,17 @@ class DownPayment(models.Model): ('payment', 'Payment'), ], string='Status Pembayaran', default='pending', tracking=3) - name_approval_departement = fields.Char(string='Approval Departement') - name_approval_ap = fields.Char(string='Approval AP') - name_approval_pimpinan = fields.Char(string='Approval Pimpinan') + 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) departement_type = fields.Selection([ ('sales', 'Sales'), @@ -192,30 +200,71 @@ class DownPayment(models.Model): # 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': if self.env.user.id != 23: # ID user AP 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.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}") + # rec.message_post(body=f"Approval oleh {self.env.user.name} pada tahap {rec.status}.") def action_reject(self): @@ -347,6 +396,18 @@ class RealizationDownPayment(models.Model): 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([ ('draft', 'Draft'), ('pengajuan1', 'Menunggu Approval Departement'), @@ -528,6 +589,75 @@ class RealizationDownPayment(models.Model): '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.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': + if self.env.user.id != 23: # ID user AP + 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.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 DownPaymentApOnly(models.TransientModel): _name = 'down.payment.ap.only' -- cgit v1.2.3 From d02c3d5d0522e6ec5a43d1380c078f0dd5fd1275 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 23 Jul 2025 11:06:12 +0700 Subject: (andri) add reject & set to draft Pengajuan PUM --- indoteknik_custom/models/down_payment.py | 55 +++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index afb8ca73..8f449566 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -95,6 +95,8 @@ class DownPayment(models.Model): 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: @@ -268,13 +270,42 @@ class DownPayment(models.Model): def action_reject(self): - # Logic untuk konfirmasi pembayaran - return + 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: - record.status = record.last_status if record.last_status else 'draft' - return + # 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 = record.last_status or 'draft' + + # 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): # if self.env.user.id != 23: @@ -657,7 +688,21 @@ class RealizationDownPayment(models.Model): # 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' -- cgit v1.2.3 From 758db1e691ca9193ce5029246c98e3b27be540f6 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 24 Jul 2025 13:19:04 +0700 Subject: (andri) add user again for validation & rev Seq No Dokumen --- indoteknik_custom/models/down_payment.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 8f449566..0263937b 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -238,7 +238,8 @@ class DownPayment(models.Model): ) elif rec.status == 'pengajuan2': - if self.env.user.id != 23: # ID user AP + 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 @@ -308,7 +309,8 @@ class DownPayment(models.Model): def action_ap_only(self): - # if self.env.user.id != 23: + # 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.') return { 'name': 'Create CAB AP Only', @@ -542,6 +544,9 @@ class RealizationDownPayment(models.Model): 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): + # 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.') self.ensure_one() if not self.pum_id or not self.pum_id.move_id: @@ -658,7 +663,8 @@ class RealizationDownPayment(models.Model): ) elif rec.status == 'pengajuan2': - if self.env.user.id != 23: # ID user AP + 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 -- cgit v1.2.3 From 1f5366bece1c1237d078c63926b5f95de49b2c28 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 25 Jul 2025 16:00:48 +0700 Subject: (andri) fix validation only AP --- indoteknik_custom/models/down_payment.py | 59 ++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 26 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 0263937b..4ed7d91c 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -105,6 +105,11 @@ class DownPayment(models.Model): 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.") @@ -309,9 +314,15 @@ class DownPayment(models.Model): def action_ap_only(self): - # 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.') + 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', @@ -395,7 +406,9 @@ class RealizationDownPaymentUseLine(models.Model): @api.onchange('done_attachment') def _onchange_done_attachment(self): - if self.done_attachment and self.env.user.id != 23: + 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': { @@ -491,6 +504,11 @@ class RealizationDownPayment(models.Model): 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.") @@ -530,7 +548,8 @@ class RealizationDownPayment(models.Model): self.ensure_one() # Validasi hanya AP yang bisa validasi - if self.env.user.id != 23: + 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': @@ -544,11 +563,14 @@ class RealizationDownPayment(models.Model): 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): - # 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.') 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.") @@ -632,10 +654,10 @@ class RealizationDownPayment(models.Model): formatted_date = now.strftime('%d %B %Y %H:%M') for rec in self: - if not rec.departement_type: + if not rec.pum_id.departement_type: raise UserError("Field 'departement_type' wajib diisi sebelum approval.") - approver_id = rec._get_departement_approver() + approver_id = rec.pum_id._get_departement_approver() if rec.status == 'pengajuan1': if self.env.user.id != approver_id: @@ -653,7 +675,7 @@ class RealizationDownPayment(models.Model): 'fat': 'Finance & Accounting Manager', 'hr_ga': 'HR & GA Manager', } - rec.position_department = department_titles.get(rec.departement_type, 'Departement Manager') + rec.position_department = department_titles.get(rec.pum_id.departement_type, 'Departement Manager') rec.status = 'pengajuan2' @@ -765,18 +787,3 @@ class DownPaymentApOnly(models.TransientModel): 'target': 'current', } - - - - - - - - - - - - - - - -- cgit v1.2.3 From 213d4a2e63f393b08b49d9b0da1f30396f717c77 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 25 Jul 2025 17:43:00 +0700 Subject: (andri) fix days remaining terhitung dari tanggal pulang hingga 7 hari kedepan + fix status --- indoteknik_custom/models/down_payment.py | 45 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 4ed7d91c..81f2044f 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -28,13 +28,23 @@ class DownPayment(models.Model): 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='Estimasi Batas Durasi Pengajuan' + string='Batas Pengajuan', + help='Tanggal batas maksimal pengajuan realisasi setelah kembali ke kantor. ' + '7 hari setelah tanggal kembali.' ) days_remaining = fields.Integer( - string='Due Date', + 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([ @@ -292,7 +302,7 @@ class DownPayment(models.Model): 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 = record.last_status or 'draft' + new_status = 'pengajuan1' # Reset field-field approval & alasan reject record.write({ @@ -336,22 +346,26 @@ class DownPayment(models.Model): } - @api.depends('create_date') + @api.depends('date_back_to_office') def _compute_days_remaining(self): today = date.today() for rec in self: - if rec.create_date: - due_date = rec.create_date.date() + timedelta(days=7) - rec.days_remaining = (due_date - today).days + 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.model def create(self, vals): if not vals.get('number') or vals['number'] == 'New Draft': vals['number'] = self.env['ir.sequence'].next_by_code('down.payment') or 'New Draft' - if not vals.get('estimated_return_date'): - vals['estimated_return_date'] = date.today() + timedelta(days=7) vals['status'] = 'pengajuan1' return super(DownPayment, self).create(vals) @@ -455,22 +469,11 @@ class RealizationDownPayment(models.Model): position_pimpinan = fields.Char(string='Position Pimpinan', tracking=True) 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') + ], string='Status', default='pengajuan1', tracking=3, index=True, track_visibility='onchange') done_status = fields.Selection([ ('remaining', 'Remaining'), -- cgit v1.2.3 From fff5507cf7c0f360a5c68068e77fa84b1d6340c5 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 26 Jul 2025 09:23:51 +0700 Subject: (andri) fix --- indoteknik_custom/models/down_payment.py | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 81f2044f..0429c4d8 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -361,11 +361,61 @@ class DownPayment(models.Model): rec.estimated_return_date = False rec.days_remaining = 0 + @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) -- cgit v1.2.3 From 9eb74d5978a1d18c3374dbc0b4358bd189cfc8e9 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 26 Jul 2025 14:50:44 +0700 Subject: (andri) add validasi input tanggal kembali --- indoteknik_custom/models/down_payment.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 0429c4d8..04dfbbc4 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -1,5 +1,5 @@ from odoo import models, api, fields, _ -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError from datetime import date, datetime, timedelta # import datetime import logging @@ -361,6 +361,16 @@ class DownPayment(models.Model): 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: -- cgit v1.2.3 From 9606b73b33a01291ecbcb9154e32212ba5692bdb Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 28 Jul 2025 12:56:06 +0700 Subject: (andri) fix status done not realized --- indoteknik_custom/models/down_payment.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 04dfbbc4..7889ac53 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -541,6 +541,8 @@ class RealizationDownPayment(models.Model): ('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 @@ -617,6 +619,7 @@ class RealizationDownPayment(models.Model): 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: @@ -650,7 +653,7 @@ class RealizationDownPayment(models.Model): account_uang_muka = 403 # Tanggal pakai create_date atau hari ini - account_date = self.create_date.date() if self.create_date else fields.Date.today() + account_date = self.date_done_not_realized ref_label = f"Realisasi {self.pum_id.number} Biaya {self.pum_id.detail_note} ({cab_move.name})" @@ -768,6 +771,7 @@ class RealizationDownPayment(models.Model): 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} " -- cgit v1.2.3 From d00a277cf3144100b2ca4b8ee640a23776537349 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 1 Aug 2025 08:42:14 +0700 Subject: (andri) add template mail for reminder PUM --- indoteknik_custom/models/down_payment.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 7889ac53..946bd792 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -72,6 +72,8 @@ class DownPayment(models.Model): 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") @@ -257,6 +259,7 @@ class DownPayment(models.Model): 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' -- cgit v1.2.3 From 8ecb66beff5da35886d821d373e9a5dd7fe5396b Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 1 Aug 2025 11:26:43 +0700 Subject: (andri) add attachment in email reminder & Testing --- indoteknik_custom/models/down_payment.py | 122 ++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 946bd792..67172dfa 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -7,13 +7,14 @@ _logger = logging.getLogger(__name__) from terbilang import Terbilang import pytz from pytz import timezone +import base64 class DownPayment(models.Model): _name = 'down.payment' _description = 'Down Payment Management' _rec_name = 'number' - _inherit = ['mail.thread'] + _inherit = ['mail.thread', 'mail.activity.mixin'] 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) @@ -73,7 +74,6 @@ class DownPayment(models.Model): 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") @@ -109,6 +109,124 @@ class DownPayment(models.Model): reason_reject = fields.Text(string='Alasan Penolakan') + currency_id = fields.Many2one( + 'res.currency', string='Currency', + default=lambda self: self.env.company.currency_id + ) + + + def _get_jasper_attachment(self): + self.ensure_one() + report = self.env['ir.actions.report'].browse(1134) # ID Downpayment Report + if not report: + raise UserError("Report Jasper tidak ditemukan.") + + data = report.render_jasper(self.ids, data={})[0] + filename = f"{self.number}.pdf" + return { + 'name': filename, + 'datas': base64.b64encode(data), + 'type': 'binary', + 'mimetype': 'application/pdf', + 'filename': filename, + } + + def action_send_pum_reminder(self): + """ + Kirim email reminder PUM otomatis. + - Hari ini = kirim dengan template 'mail_template_pum_reminder_today' + - H-2 dari due date = kirim dengan template 'mail_template_pum_reminder_h_2' + """ + today = date.today() + pum_ids = self.search([ + ('date_back_to_office', '!=', False), + ('status', 'not in', ['draft', 'reject']), + ]) + + template_today = self.env.ref('indoteknik_custom.mail_template_pum_reminder_today', raise_if_not_found=False) + template_h2 = self.env.ref('indoteknik_custom.mail_template_pum_reminder_h_2', raise_if_not_found=False) + + if not template_today or not template_h2: + _logger.warning("Salah satu template email tidak ditemukan.") + return + + for pum in pum_ids: + _logger.info(f"[REMINDER] Memproses PUM {pum.number}") + + if not pum.email_ap or not pum.user_id.partner_id.email: + _logger.warning(f"[REMINDER] Lewati PUM {pum.number} karena email_ap atau email user kosong.") + continue + + due_date = pum.date_back_to_office + timedelta(days=7) + days_remaining = (due_date - today).days + + realization = self.env['realization.down.payment'].search([('pum_id', '=', pum.id)], limit=1) + if not realization or realization.done_status != 'remaining': + _logger.info(f"[REMINDER] Lewati PUM {pum.number}, status realisasi bukan 'remaining'.") + continue + + # Tentukan template + if pum.date_back_to_office == today: + template = template_today + elif days_remaining == 2: + template = template_h2 + else: + _logger.info(f"[REMINDER] Lewati PUM {pum.number}, hari ini bukan tanggal pengingat.") + continue + + # Generate attachment + try: + attachment_vals = pum._get_jasper_attachment() + attachment = self.env['ir.attachment'].create({ + 'name': attachment_vals['name'], + 'type': 'binary', + 'datas': attachment_vals['datas'], + 'res_model': 'down.payment', + 'res_id': pum.id, + 'mimetype': 'application/pdf', + }) + except Exception as e: + _logger.error(f"[REMINDER] Gagal membuat attachment untuk PUM {pum.number}: {str(e)}") + continue + + email_values = { + # 'email_to': pum.user_id.partner_id.email, + 'email_to': 'andrifebriyadiputra@gmail.com', + 'email_from': pum.email_ap, + 'attachment_ids': [(6, 0, [attachment.id])], + } + + _logger.info(f"[REMINDER] Mengirim email PUM {pum.number} ke {email_values['email_to']} dari {email_values['email_from']}") + + try: + body_html = template._render_field('body_html', [pum.id])[pum.id] + + template.send_mail(pum.id, force_send=True, email_values=email_values) + _logger.info(f"[REMINDER] Email berhasil dikirim untuk PUM {pum.number}") + + # Post info sederhana + pum.message_post( + body="Email Reminder Berhasil dikirimkan", + message_type="comment", + subtype_xmlid="mail.mt_note", + ) + + user_system = self.env['res.users'].browse(25) + system_id = user_system.partner_id.id if user_system else False + + # Post isi email ke chatter + pum.message_post( + body=body_html, + message_type="comment", + subtype_xmlid="mail.mt_note", + author_id=system_id, + ) + except Exception as e: + _logger.error(f"[REMINDER] Gagal mengirim email untuk PUM {pum.number}: {str(e)}") + + return True + + @api.depends('move_id.state') def _compute_is_cab_visible(self): for rec in self: -- cgit v1.2.3 From 216fe79c11dace298b260be946f14a3af5b5a929 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 4 Aug 2025 13:57:34 +0700 Subject: (andri) add field approved by --- indoteknik_custom/models/down_payment.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 67172dfa..1fbac44a 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -84,6 +84,8 @@ class DownPayment(models.Model): position_ap = fields.Char(string='Position AP', tracking=True) position_pimpinan = fields.Char(string='Position Pimpinan', tracking=True) + approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') + departement_type = fields.Selection([ ('sales', 'Sales'), ('merchandiser', 'Merchandiser'), @@ -351,6 +353,7 @@ class DownPayment(models.Model): 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_departement rec.date_approved_department = now # Mapping posisi berdasarkan departement_type @@ -377,6 +380,7 @@ class DownPayment(models.Model): 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap rec.email_ap = self.env.user.email rec.date_approved_ap = now rec.position_ap = 'Finance AP' @@ -391,6 +395,7 @@ class DownPayment(models.Model): 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_pimpinan rec.date_approved_pimpinan = now rec.position_pimpinan = 'Pimpinan' rec.status = 'approved' @@ -649,6 +654,8 @@ class RealizationDownPayment(models.Model): position_ap = fields.Char(string='Position AP', tracking=True) position_pimpinan = fields.Char(string='Position Pimpinan', tracking=True) + approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') + status = fields.Selection([ ('pengajuan1', 'Menunggu Approval Departement'), ('pengajuan2', 'Menunggu Pengecekan AP'), @@ -850,6 +857,7 @@ class RealizationDownPayment(models.Model): 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_departement rec.date_approved_department = now # Mapping posisi berdasarkan departement_type @@ -876,6 +884,7 @@ class RealizationDownPayment(models.Model): 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap rec.date_approved_ap = now rec.position_ap = 'Finance AP' rec.status = 'pengajuan3' @@ -889,6 +898,7 @@ class RealizationDownPayment(models.Model): 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_pimpinan rec.date_approved_pimpinan = now rec.position_pimpinan = 'Pimpinan' rec.status = 'approved' -- cgit v1.2.3 From d1a529a1cb034086b0581b110c48785e28bf3a8c Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 5 Aug 2025 17:10:21 +0700 Subject: (andri) add validasi confirm payment only AP --- indoteknik_custom/models/down_payment.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 1fbac44a..ab30337a 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -318,12 +318,26 @@ class DownPayment(models.Model): def action_confirm_payment(self): + 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.') + 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).') - + raise UserError( + f'Tidak bisa konfirmasi pembayaran PUM {rec.name or ""} ' + f'karena belum ada bukti attachment (PDF/Image).' + ) + rec.status_pay_down_payment = 'payment' + rec.message_post( + body="Status pembayaran telah dikonfirmasi oleh AP.", + message_type="comment", + subtype_xmlid="mail.mt_note", + ) + + # def action_approval_check(self): # for record in self: -- cgit v1.2.3 From fcae9b43dc261223b242265141353974300a304f Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 6 Aug 2025 09:37:34 +0700 Subject: (andri) banyaknya attachment --- indoteknik_custom/models/down_payment.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index ab30337a..8d70aa69 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -590,6 +590,12 @@ class RealizationDownPaymentUseLine(models.Model): nominal = fields.Float(string='Nominal', required=True) done_attachment = fields.Boolean(string='Checked', default=False) + lot_of_attachment = fields.Selection( + related='realization_id.lot_of_attachment', + string='Lot of Attachment (Related)', + store=False + ) + attachment_type = fields.Selection([ ('pdf', 'PDF'), ('image', 'Image'), @@ -640,7 +646,7 @@ class RealizationDownPayment(models.Model): 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,) + related = fields.Char(string='Terkait', tracking=3) pemberian_line_ids = fields.One2many( 'realization.down.payment.line', 'realization_id', string='Rincian Pemberian' @@ -700,9 +706,24 @@ class RealizationDownPayment(models.Model): ('image', 'Image'), ], string="Attachment Type", default='pdf') + lot_of_attachment = fields.Selection([ + ('one_for_all_line', '1 Attachment Untuk Semua Line Penggunaan PUM'), + ('one_for_one_line', '1 Attachment per 1 Line Penggunaan PUM'), + ], string = "Banyaknya Attachment", default='one_for_one_line') + 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.onchange('lot_of_attachment') + def _onchange_lot_of_attachment(self): + if self.lot_of_attachment == 'one_for_all_line': + for line in self.penggunaan_line_ids: + line.attachment_file_pdf = False + line.attachment_file_image = False + line.attachment_filename_pdf = False + line.attachment_filename_image = False + + @api.depends('move_id.state') def _compute_is_cab_visible(self): for rec in self: -- cgit v1.2.3 From 33621956bdb9d807b480eda44ce7f2152f508695 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 6 Aug 2025 11:22:37 +0700 Subject: (andri) add button check/uncheck all line use PUM --- indoteknik_custom/models/down_payment.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 8d70aa69..5c7c3f49 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -714,6 +714,22 @@ class RealizationDownPayment(models.Model): 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') + def action_toggle_check_attachment(self): + # ap_user_ids = [23, 9468] + # if self.env.user.id not in ap_user_ids: + # raise UserError('Hanya User AP yang dapat menggunakan tombol ini.') + + for rec in self: + if not rec.penggunaan_line_ids: + continue + + if all(line.done_attachment for line in rec.penggunaan_line_ids): + for line in rec.penggunaan_line_ids: + line.done_attachment = False + else: + for line in rec.penggunaan_line_ids: + line.done_attachment = True + @api.onchange('lot_of_attachment') def _onchange_lot_of_attachment(self): if self.lot_of_attachment == 'one_for_all_line': -- cgit v1.2.3 From 2f31e36104c0b92bf022dc78b3952f08815dd1cc Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 9 Aug 2025 16:08:18 +0700 Subject: (andri) fix validasi sisa PUM & nominal --- indoteknik_custom/models/down_payment.py | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 5c7c3f49..e0828968 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -116,6 +116,19 @@ class DownPayment(models.Model): default=lambda self: self.env.company.currency_id ) + @api.onchange('nominal') + def _onchange_nominal_no_minus(self): + if self.nominal and self.nominal < 0: + self.nominal = 0 + return { + 'warning': { + 'title': _('Nominal Tidak Valid'), + 'message': _( + "Nominal tidak boleh diisi minus.\n" + "Nilai di set menjadi nol." + ) + } + } def _get_jasper_attachment(self): self.ensure_one() @@ -636,6 +649,20 @@ class RealizationDownPaymentUseLine(models.Model): 'message': _('Hanya user AP yang bisa mencentang Done Attachment.') } } + + @api.onchange('nominal') + def _onchange_nominal_no_minus(self): + if self.nominal and self.nominal < 0: + self.nominal = 0 + return { + 'warning': { + 'title': _('Nominal Tidak Valid'), + 'message': _( + "Nominal penggunaan PUM tidak boleh diisi minus.\n" + "Nilai di Set menjadi nol." + ) + } + } class RealizationDownPayment(models.Model): _name = 'realization.down.payment' @@ -965,6 +992,26 @@ class RealizationDownPayment(models.Model): # rec.message_post(body=f"Approval oleh {self.env.user.name} pada tahap {rec.status}.") + def _check_remaining_value(self): + for rec in self: + # Cek sisa PUM + if rec.remaining_value < 0: + raise ValidationError( + "Sisa uang PUM tidak boleh kurang dari 0.\n" + "Jika ada penggunaan uang pribadi, maka ajukan dengan sistem reimburse." + ) + + @api.model + def create(self, vals): + rec = super().create(vals) + rec._check_remaining_value() + return rec + + def write(self, vals): + res = super().write(vals) + self._check_remaining_value() + return res + class RejectReasonDownPayment(models.TransientModel): _name = 'reject.reason.downpayment' _description = 'Wizard for Reject Reason Down Payment' -- cgit v1.2.3 From e7c4b8161fde799cba05f371018315776539b479 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 18 Aug 2025 09:27:16 +0700 Subject: (andri) sisa hari pengajuan tidak dihitung lagi jika status approved dan reject --- indoteknik_custom/models/down_payment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index e0828968..9c200cca 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -499,10 +499,13 @@ class DownPayment(models.Model): } - @api.depends('date_back_to_office') + @api.depends('date_back_to_office', 'status') def _compute_days_remaining(self): today = date.today() for rec in self: + if rec.status in ['approved', 'reject'] and rec.days_remaining: + continue + if rec.date_back_to_office: due_date = rec.date_back_to_office + timedelta(days=7) rec.estimated_return_date = due_date -- cgit v1.2.3 From 56521e04d3ea44d9f0115ef327b48e2c97f92fcc Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 18 Aug 2025 13:39:21 +0700 Subject: (andri) fix --- indoteknik_custom/models/down_payment.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py index 9c200cca..1152fcbf 100644 --- a/indoteknik_custom/models/down_payment.py +++ b/indoteknik_custom/models/down_payment.py @@ -745,9 +745,9 @@ class RealizationDownPayment(models.Model): is_cab_visible = fields.Boolean(string='Is Journal Uang Muka Visible', compute='_compute_is_cab_visible') def action_toggle_check_attachment(self): - # ap_user_ids = [23, 9468] - # if self.env.user.id not in ap_user_ids: - # raise UserError('Hanya User AP yang dapat menggunakan tombol ini.') + ap_user_ids = [23, 9468] + if self.env.user.id not in ap_user_ids: + raise UserError('Hanya User AP yang dapat menggunakan tombol ini.') for rec in self: if not rec.penggunaan_line_ids: @@ -862,7 +862,7 @@ class RealizationDownPayment(models.Model): account_uang_muka = 403 # Tanggal pakai create_date atau hari ini - account_date = self.date_done_not_realized + account_date = self.date_done_not_realized or fields.Date.today() ref_label = f"Realisasi {self.pum_id.number} Biaya {self.pum_id.detail_note} ({cab_move.name})" -- cgit v1.2.3 From 96cb47f78e7847bb7d0ceec20fc8d275e9a916d8 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 2 Oct 2025 15:08:54 +0700 Subject: (andri) rename downpayment to advance payment request --- indoteknik_custom/models/__init__.py | 2 +- .../models/advance_payment_request.py | 1087 ++++++++++++++++++++ 2 files changed, 1088 insertions(+), 1 deletion(-) create mode 100644 indoteknik_custom/models/advance_payment_request.py (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index f3501f7b..71e5b4b3 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -154,7 +154,7 @@ from . import approval_invoice_date from . import approval_payment_term from . import refund_sale_order # from . import patch -from . import down_payment +from . import advance_payment_request from . import tukar_guling from . import tukar_guling_po from . import update_date_planned_po_wizard diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py new file mode 100644 index 00000000..ccbf8988 --- /dev/null +++ b/indoteknik_custom/models/advance_payment_request.py @@ -0,0 +1,1087 @@ +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 +import base64 + + +class AdvancePaymentRequest(models.Model): + _name = 'advance.payment.request' + _description = 'Advance Payment Request' + _rec_name = 'number' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + 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') + 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) + + approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') + + 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') + + currency_id = fields.Many2one( + 'res.currency', string='Currency', + default=lambda self: self.env.company.currency_id + ) + + @api.onchange('nominal') + def _onchange_nominal_no_minus(self): + if self.nominal and self.nominal < 0: + self.nominal = 0 + return { + 'warning': { + 'title': _('Nominal Tidak Valid'), + 'message': _( + "Nominal tidak boleh diisi minus.\n" + "Nilai di set menjadi nol." + ) + } + } + + def _get_jasper_attachment(self): + self.ensure_one() + report = self.env['ir.actions.report'].browse(1134) # ID Downpayment Report + if not report: + raise UserError("Report Jasper tidak ditemukan.") + + data = report.render_jasper(self.ids, data={})[0] + filename = f"{self.number}.pdf" + return { + 'name': filename, + 'datas': base64.b64encode(data), + 'type': 'binary', + 'mimetype': 'application/pdf', + 'filename': filename, + } + + def action_send_pum_reminder(self): + """ + Kirim email reminder PUM otomatis. + - Hari ini = kirim dengan template 'mail_template_pum_reminder_today' + - H-2 dari due date = kirim dengan template 'mail_template_pum_reminder_h_2' + """ + today = date.today() + pum_ids = self.search([ + ('date_back_to_office', '!=', False), + ('status', 'not in', ['draft', 'reject']), + ]) + + template_today = self.env.ref('indoteknik_custom.mail_template_pum_reminder_today', raise_if_not_found=False) + template_h2 = self.env.ref('indoteknik_custom.mail_template_pum_reminder_h_2', raise_if_not_found=False) + + if not template_today or not template_h2: + _logger.warning("Salah satu template email tidak ditemukan.") + return + + for pum in pum_ids: + _logger.info(f"[REMINDER] Memproses PUM {pum.number}") + + if not pum.email_ap or not pum.user_id.partner_id.email: + _logger.warning(f"[REMINDER] Lewati PUM {pum.number} karena email_ap atau email user kosong.") + continue + + due_date = pum.date_back_to_office + timedelta(days=7) + days_remaining = (due_date - today).days + + realization = self.env['advance.payment.settlement'].search([('pum_id', '=', pum.id)], limit=1) + if not realization or realization.done_status != 'remaining': + _logger.info(f"[REMINDER] Lewati PUM {pum.number}, status realisasi bukan 'remaining'.") + continue + + # Tentukan template + if pum.date_back_to_office == today: + template = template_today + elif days_remaining == 2: + template = template_h2 + else: + _logger.info(f"[REMINDER] Lewati PUM {pum.number}, hari ini bukan tanggal pengingat.") + continue + + # Generate attachment + try: + attachment_vals = pum._get_jasper_attachment() + attachment = self.env['ir.attachment'].create({ + 'name': attachment_vals['name'], + 'type': 'binary', + 'datas': attachment_vals['datas'], + 'res_model': 'advance.payment.request', + 'res_id': pum.id, + 'mimetype': 'application/pdf', + }) + except Exception as e: + _logger.error(f"[REMINDER] Gagal membuat attachment untuk PUM {pum.number}: {str(e)}") + continue + + email_values = { + # 'email_to': pum.user_id.partner_id.email, + 'email_to': 'andrifebriyadiputra@gmail.com', + 'email_from': pum.email_ap, + 'attachment_ids': [(6, 0, [attachment.id])], + } + + _logger.info(f"[REMINDER] Mengirim email PUM {pum.number} ke {email_values['email_to']} dari {email_values['email_from']}") + + try: + body_html = template._render_field('body_html', [pum.id])[pum.id] + + template.send_mail(pum.id, force_send=True, email_values=email_values) + _logger.info(f"[REMINDER] Email berhasil dikirim untuk PUM {pum.number}") + + # Post info sederhana + pum.message_post( + body="Email Reminder Berhasil dikirimkan", + message_type="comment", + subtype_xmlid="mail.mt_note", + ) + + user_system = self.env['res.users'].browse(25) + system_id = user_system.partner_id.id if user_system else False + + # Post isi email ke chatter + pum.message_post( + body=body_html, + message_type="comment", + subtype_xmlid="mail.mt_note", + author_id=system_id, + ) + except Exception as e: + _logger.error(f"[REMINDER] Gagal mengirim email untuk PUM {pum.number}: {str(e)}") + + return True + + + @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['advance.payment.settlement'].search([('pum_id', '=', self.id)], limit=1) + + if realization: + return { + 'type': 'ir.actions.act_window', + 'name': 'Realisasi PUM', + 'res_model': 'advance.payment.settlement', + 'view_mode': 'form', + 'target': 'current', + 'res_id': realization.id, + } + else: + return { + 'type': 'ir.actions.act_window', + 'name': 'Realisasi PUM', + 'res_model': 'advance.payment.settlement', + '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): + 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.') + + for rec in self: + if not rec.attachment_file_image and not rec.attachment_file_pdf: + raise UserError( + f'Tidak bisa konfirmasi pembayaran PUM {rec.name or ""} ' + f'karena belum ada bukti attachment (PDF/Image).' + ) + + rec.status_pay_down_payment = 'payment' + + rec.message_post( + body="Status pembayaran telah dikonfirmasi oleh AP.", + message_type="comment", + subtype_xmlid="mail.mt_note", + ) + + + + # 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_departement + 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap + 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_pimpinan + 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': 'advance.payment.request.reject', + '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': 'advance.payment.create.bill', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_nominal': self.nominal, + 'default_down_payment_id': self.id, + } + } + + + @api.depends('date_back_to_office', 'status') + def _compute_days_remaining(self): + today = date.today() + for rec in self: + if rec.status in ['approved', 'reject'] and rec.days_remaining: + continue + + 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['advance.payment.settlement'].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['advance.payment.settlement'].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('advance.payment.request') or 'New Draft' + + vals['status'] = 'pengajuan1' + return super(AdvancePaymentRequest, self).create(vals) + + +class AdvancePaymentSettlementLine(models.Model): + _name = 'advance.payment.settlement.line' + _description = 'Advance Payment Settlement Line' + + realization_id = fields.Many2one('advance.payment.settlement', 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 AdvancePaymentUsageLine(models.Model): + _name = 'advance.payment.usage.line' + _description = 'Advance Payment Usage Line' + + realization_id = fields.Many2one('advance.payment.settlement', 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) + + lot_of_attachment = fields.Selection( + related='realization_id.lot_of_attachment', + string='Lot of Attachment (Related)', + store=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.') + } + } + + @api.onchange('nominal') + def _onchange_nominal_no_minus(self): + if self.nominal and self.nominal < 0: + self.nominal = 0 + return { + 'warning': { + 'title': _('Nominal Tidak Valid'), + 'message': _( + "Nominal penggunaan PUM tidak boleh diisi minus.\n" + "Nilai di Set menjadi nol." + ) + } + } + +class AdvancePaymentSettlement(models.Model): + _name = 'advance.payment.settlement' + _description = 'Advance Payment Settlement' + _inherit = ['mail.thread'] + + pum_id = fields.Many2one('advance.payment.request', 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( + 'advance.payment.settlement.line', 'realization_id', string='Rincian Pemberian' + ) + penggunaan_line_ids = fields.One2many( + 'advance.payment.usage.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) + + approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') + + 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') + + lot_of_attachment = fields.Selection([ + ('one_for_all_line', '1 Attachment Untuk Semua Line Penggunaan PUM'), + ('one_for_one_line', '1 Attachment per 1 Line Penggunaan PUM'), + ], string = "Banyaknya Attachment", default='one_for_one_line') + + 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') + + def action_toggle_check_attachment(self): + ap_user_ids = [23, 9468] + if self.env.user.id not in ap_user_ids: + raise UserError('Hanya User AP yang dapat menggunakan tombol ini.') + + for rec in self: + if not rec.penggunaan_line_ids: + continue + + if all(line.done_attachment for line in rec.penggunaan_line_ids): + for line in rec.penggunaan_line_ids: + line.done_attachment = False + else: + for line in rec.penggunaan_line_ids: + line.done_attachment = True + + @api.onchange('lot_of_attachment') + def _onchange_lot_of_attachment(self): + if self.lot_of_attachment == 'one_for_all_line': + for line in self.penggunaan_line_ids: + line.attachment_file_pdf = False + line.attachment_file_image = False + line.attachment_filename_pdf = False + line.attachment_filename_image = False + + + @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 or fields.Date.today() + + 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_departement + 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap + 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_pimpinan + 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}.") + + def _check_remaining_value(self): + for rec in self: + # Cek sisa PUM + if rec.remaining_value < 0: + raise ValidationError( + "Sisa uang PUM tidak boleh kurang dari 0.\n" + "Jika ada penggunaan uang pribadi, maka ajukan dengan sistem reimburse." + ) + + @api.model + def create(self, vals): + rec = super().create(vals) + rec._check_remaining_value() + return rec + + def write(self, vals): + res = super().write(vals) + self._check_remaining_value() + return res + +class AdvancePaymentRequestReject(models.TransientModel): + _name = 'advance.payment.request.reject' + _description = 'Wizard for Reject Reason APR' + + request_id = fields.Many2one('advance.payment.request', 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 AdvancePaymentCreateBill(models.TransientModel): + _name = 'advance.payment.create.bill' + _description = 'Create Bill from Advance Payment' + + down_payment_id = fields.Many2one('advance.payment.request', 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', + } \ No newline at end of file -- cgit v1.2.3 From 04a7f5794a4964f258072bf10c8345e98c911572 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 2 Oct 2025 15:44:55 +0700 Subject: (andri) delete downpayment --- indoteknik_custom/models/down_payment.py | 1088 ------------------------------ 1 file changed, 1088 deletions(-) delete mode 100644 indoteknik_custom/models/down_payment.py (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/down_payment.py b/indoteknik_custom/models/down_payment.py deleted file mode 100644 index 1152fcbf..00000000 --- a/indoteknik_custom/models/down_payment.py +++ /dev/null @@ -1,1088 +0,0 @@ -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 -import base64 - - -class DownPayment(models.Model): - _name = 'down.payment' - _description = 'Down Payment Management' - _rec_name = 'number' - _inherit = ['mail.thread', 'mail.activity.mixin'] - - 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') - 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) - - approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') - - 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') - - currency_id = fields.Many2one( - 'res.currency', string='Currency', - default=lambda self: self.env.company.currency_id - ) - - @api.onchange('nominal') - def _onchange_nominal_no_minus(self): - if self.nominal and self.nominal < 0: - self.nominal = 0 - return { - 'warning': { - 'title': _('Nominal Tidak Valid'), - 'message': _( - "Nominal tidak boleh diisi minus.\n" - "Nilai di set menjadi nol." - ) - } - } - - def _get_jasper_attachment(self): - self.ensure_one() - report = self.env['ir.actions.report'].browse(1134) # ID Downpayment Report - if not report: - raise UserError("Report Jasper tidak ditemukan.") - - data = report.render_jasper(self.ids, data={})[0] - filename = f"{self.number}.pdf" - return { - 'name': filename, - 'datas': base64.b64encode(data), - 'type': 'binary', - 'mimetype': 'application/pdf', - 'filename': filename, - } - - def action_send_pum_reminder(self): - """ - Kirim email reminder PUM otomatis. - - Hari ini = kirim dengan template 'mail_template_pum_reminder_today' - - H-2 dari due date = kirim dengan template 'mail_template_pum_reminder_h_2' - """ - today = date.today() - pum_ids = self.search([ - ('date_back_to_office', '!=', False), - ('status', 'not in', ['draft', 'reject']), - ]) - - template_today = self.env.ref('indoteknik_custom.mail_template_pum_reminder_today', raise_if_not_found=False) - template_h2 = self.env.ref('indoteknik_custom.mail_template_pum_reminder_h_2', raise_if_not_found=False) - - if not template_today or not template_h2: - _logger.warning("Salah satu template email tidak ditemukan.") - return - - for pum in pum_ids: - _logger.info(f"[REMINDER] Memproses PUM {pum.number}") - - if not pum.email_ap or not pum.user_id.partner_id.email: - _logger.warning(f"[REMINDER] Lewati PUM {pum.number} karena email_ap atau email user kosong.") - continue - - due_date = pum.date_back_to_office + timedelta(days=7) - days_remaining = (due_date - today).days - - realization = self.env['realization.down.payment'].search([('pum_id', '=', pum.id)], limit=1) - if not realization or realization.done_status != 'remaining': - _logger.info(f"[REMINDER] Lewati PUM {pum.number}, status realisasi bukan 'remaining'.") - continue - - # Tentukan template - if pum.date_back_to_office == today: - template = template_today - elif days_remaining == 2: - template = template_h2 - else: - _logger.info(f"[REMINDER] Lewati PUM {pum.number}, hari ini bukan tanggal pengingat.") - continue - - # Generate attachment - try: - attachment_vals = pum._get_jasper_attachment() - attachment = self.env['ir.attachment'].create({ - 'name': attachment_vals['name'], - 'type': 'binary', - 'datas': attachment_vals['datas'], - 'res_model': 'down.payment', - 'res_id': pum.id, - 'mimetype': 'application/pdf', - }) - except Exception as e: - _logger.error(f"[REMINDER] Gagal membuat attachment untuk PUM {pum.number}: {str(e)}") - continue - - email_values = { - # 'email_to': pum.user_id.partner_id.email, - 'email_to': 'andrifebriyadiputra@gmail.com', - 'email_from': pum.email_ap, - 'attachment_ids': [(6, 0, [attachment.id])], - } - - _logger.info(f"[REMINDER] Mengirim email PUM {pum.number} ke {email_values['email_to']} dari {email_values['email_from']}") - - try: - body_html = template._render_field('body_html', [pum.id])[pum.id] - - template.send_mail(pum.id, force_send=True, email_values=email_values) - _logger.info(f"[REMINDER] Email berhasil dikirim untuk PUM {pum.number}") - - # Post info sederhana - pum.message_post( - body="Email Reminder Berhasil dikirimkan", - message_type="comment", - subtype_xmlid="mail.mt_note", - ) - - user_system = self.env['res.users'].browse(25) - system_id = user_system.partner_id.id if user_system else False - - # Post isi email ke chatter - pum.message_post( - body=body_html, - message_type="comment", - subtype_xmlid="mail.mt_note", - author_id=system_id, - ) - except Exception as e: - _logger.error(f"[REMINDER] Gagal mengirim email untuk PUM {pum.number}: {str(e)}") - - return True - - - @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): - 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.') - - for rec in self: - if not rec.attachment_file_image and not rec.attachment_file_pdf: - raise UserError( - f'Tidak bisa konfirmasi pembayaran PUM {rec.name or ""} ' - f'karena belum ada bukti attachment (PDF/Image).' - ) - - rec.status_pay_down_payment = 'payment' - - rec.message_post( - body="Status pembayaran telah dikonfirmasi oleh AP.", - message_type="comment", - subtype_xmlid="mail.mt_note", - ) - - - - # 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_departement - 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap - 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_pimpinan - 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', 'status') - def _compute_days_remaining(self): - today = date.today() - for rec in self: - if rec.status in ['approved', 'reject'] and rec.days_remaining: - continue - - 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) - - lot_of_attachment = fields.Selection( - related='realization_id.lot_of_attachment', - string='Lot of Attachment (Related)', - store=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.') - } - } - - @api.onchange('nominal') - def _onchange_nominal_no_minus(self): - if self.nominal and self.nominal < 0: - self.nominal = 0 - return { - 'warning': { - 'title': _('Nominal Tidak Valid'), - 'message': _( - "Nominal penggunaan PUM tidak boleh diisi minus.\n" - "Nilai di Set menjadi nol." - ) - } - } - -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) - - approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') - - 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') - - lot_of_attachment = fields.Selection([ - ('one_for_all_line', '1 Attachment Untuk Semua Line Penggunaan PUM'), - ('one_for_one_line', '1 Attachment per 1 Line Penggunaan PUM'), - ], string = "Banyaknya Attachment", default='one_for_one_line') - - 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') - - def action_toggle_check_attachment(self): - ap_user_ids = [23, 9468] - if self.env.user.id not in ap_user_ids: - raise UserError('Hanya User AP yang dapat menggunakan tombol ini.') - - for rec in self: - if not rec.penggunaan_line_ids: - continue - - if all(line.done_attachment for line in rec.penggunaan_line_ids): - for line in rec.penggunaan_line_ids: - line.done_attachment = False - else: - for line in rec.penggunaan_line_ids: - line.done_attachment = True - - @api.onchange('lot_of_attachment') - def _onchange_lot_of_attachment(self): - if self.lot_of_attachment == 'one_for_all_line': - for line in self.penggunaan_line_ids: - line.attachment_file_pdf = False - line.attachment_file_image = False - line.attachment_filename_pdf = False - line.attachment_filename_image = False - - - @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 or fields.Date.today() - - 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_departement - 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap - 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_pimpinan - 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}.") - - def _check_remaining_value(self): - for rec in self: - # Cek sisa PUM - if rec.remaining_value < 0: - raise ValidationError( - "Sisa uang PUM tidak boleh kurang dari 0.\n" - "Jika ada penggunaan uang pribadi, maka ajukan dengan sistem reimburse." - ) - - @api.model - def create(self, vals): - rec = super().create(vals) - rec._check_remaining_value() - return rec - - def write(self, vals): - res = super().write(vals) - self._check_remaining_value() - return res - -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', - } - -- cgit v1.2.3 From 017c502380045ee8212932b25f9f94ee2e7b0c40 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 7 Oct 2025 13:19:52 +0700 Subject: (andri) add type request --- indoteknik_custom/models/advance_payment_request.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index ccbf8988..01c60cb4 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -116,6 +116,10 @@ class AdvancePaymentRequest(models.Model): default=lambda self: self.env.company.currency_id ) + type_request = fields.Selection([ + ('pum', 'PUM'), + ('reimburse', 'Reimburse')], string='Tipe Pengajuan', default='pum', tracking=3) + @api.onchange('nominal') def _onchange_nominal_no_minus(self): if self.nominal and self.nominal < 0: -- cgit v1.2.3 From 7a8e08aec92009eda7aac0b4a5641e899eac8c28 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 7 Oct 2025 14:17:55 +0700 Subject: (andri) tambah posisi --- indoteknik_custom/models/advance_payment_request.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 01c60cb4..1b5f2c2f 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -119,6 +119,12 @@ class AdvancePaymentRequest(models.Model): type_request = fields.Selection([ ('pum', 'PUM'), ('reimburse', 'Reimburse')], string='Tipe Pengajuan', default='pum', tracking=3) + + position_type = fields.Selection([ + ('staff', 'Staff'), + ('manager', 'Manager'), + ('pimpinan', 'Pimpinan')], string='Jabatan') + @api.onchange('nominal') def _onchange_nominal_no_minus(self): -- cgit v1.2.3 From aa754e863440d7fb8884649e53a5961124a4dbbd Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 8 Oct 2025 14:43:49 +0700 Subject: (andri) fix view + dll --- .../models/advance_payment_request.py | 28 ++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 1b5f2c2f..a0fb4d51 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -125,6 +125,10 @@ class AdvancePaymentRequest(models.Model): ('manager', 'Manager'), ('pimpinan', 'Pimpinan')], string='Jabatan') + settlement_type = fields.Selection([ + ('no_settlement', 'Belum Realisasi'), + ('settlement', 'Realisasi') + ]) @api.onchange('nominal') def _onchange_nominal_no_minus(self): @@ -292,8 +296,8 @@ class AdvancePaymentRequest(models.Model): # --------------------------------------- # AP : Manzila (Finance) ID 23 - def _get_departement_approver(self): - mapping = { + def _get_approver_mapping(self): + return { 'sales': 19, 'merchandiser': 19, 'marketing': 216, @@ -302,8 +306,28 @@ class AdvancePaymentRequest(models.Model): 'fat': 28, 'hr_ga': 7, } + + def _get_departement_approver(self): + mapping = self._get_approver_mapping() return mapping.get(self.departement_type) + @api.model + def default_get(self, fields_list): + defaults = super(AdvancePaymentRequest, self).default_get(fields_list) + user_id = defaults.get('user_id', self.env.uid) + mapping = self._get_approver_mapping() + manager_ids = set(mapping.values()) + pimpinan_id = 7 + + position = 'staff' + if user_id == pimpinan_id: + position = 'pimpinan' + elif user_id in manager_ids: + position = 'manager' + + defaults['position_type'] = position + return defaults + def action_realisasi_pum(self): self.ensure_one() -- cgit v1.2.3 From 585fefbd00baf8a6fca7aeac1c845a7400fb5d2b Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 8 Oct 2025 15:57:04 +0700 Subject: (andri) fix --- indoteknik_custom/models/advance_payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index a0fb4d51..6a0929ae 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -12,7 +12,7 @@ import base64 class AdvancePaymentRequest(models.Model): _name = 'advance.payment.request' - _description = 'Advance Payment Request' + _description = 'Advance Payment Request & Reimburse' _rec_name = 'number' _inherit = ['mail.thread', 'mail.activity.mixin'] -- cgit v1.2.3 From ff4ec105fbd2cffda3b6d8bef2b4549561ba93e7 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 9 Oct 2025 16:36:46 +0700 Subject: (andri) change type field + add field PUM perjalanan --- indoteknik_custom/models/advance_payment_request.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 6a0929ae..a867ddae 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -21,11 +21,13 @@ class AdvancePaymentRequest(models.Model): number = fields.Char(string='No. Dokumen', default='New Draft', tracking=3) - applicant_name = fields.Char(string='Nama Pemohon', tracking=3, required=True) + applicant_name = fields.Many2one('res.users', string='Nama Pemohon', required=True, tracking=3, domain="[('groups_id', 'in', [1])]") + # applicant_name = fields.One2many(string='Nama Pemohon', related='res.users') 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) + account_name = fields.Many2one('res.users', string='Nama Account', required=True, tracking=3, domain="[('groups_id', 'in', [1])]") + # 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) @@ -92,7 +94,7 @@ class AdvancePaymentRequest(models.Model): ('marketing', 'Marketing'), ('logistic', 'Logistic'), ('procurement', 'Procurement'), - ('fat', 'FAT'), + ('fat_it', 'FAT & IT'), ('hr_ga', 'HR & GA'), ], string='Departement Type', tracking=3, required=True) @@ -104,7 +106,7 @@ class AdvancePaymentRequest(models.Model): attachment_type = fields.Selection([ ('pdf', 'PDF'), ('image', 'Image'), - ], string="Attachment Type", default='pdf') + ], string="Attachment Type") 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') @@ -130,6 +132,8 @@ class AdvancePaymentRequest(models.Model): ('settlement', 'Realisasi') ]) + apr_perjalanan = fields.Boolean(string = "PUM Perjalanan?", default = False) + @api.onchange('nominal') def _onchange_nominal_no_minus(self): if self.nominal and self.nominal < 0: @@ -564,12 +568,12 @@ class AdvancePaymentRequest(models.Model): @api.onchange('applicant_name') def _onchange_applicant_name(self): if self.applicant_name: - self.account_name = self.applicant_name + self.account_name = self.applicant_name.id @api.onchange('account_name') def _onchange_account_name(self): if self.account_name: - self.applicant_name = self.account_name + self.applicant_name = self.account_name.id @api.onchange('user_id') def _onchange_user_id_limit_check(self): -- cgit v1.2.3 From ede22cbc80eed8b142457fe9779dcd61374f5c93 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 10 Oct 2025 09:30:55 +0700 Subject: (andri) penyesuaian jabatan dan department + fix view --- .../models/advance_payment_request.py | 61 ++++++++++++++-------- 1 file changed, 40 insertions(+), 21 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index a867ddae..578829f2 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -33,8 +33,7 @@ class AdvancePaymentRequest(models.Model): date_back_to_office = fields.Date( string='Tanggal Kembali ke Kantor', - tracking=3, - required=True + tracking=3 ) estimated_return_date = fields.Date( @@ -53,7 +52,7 @@ class AdvancePaymentRequest(models.Model): status = fields.Selection([ ('draft', 'Draft'), ('pengajuan1', 'Menunggu Approval Departement'), - ('pengajuan2', 'Menunggu Pengecekan AP'), + ('pengajuan2', 'Menunggu Approval AP'), ('pengajuan3', 'Menunggu Approval Pimpinan'), ('approved', 'Approved'), ('reject', 'Rejected') @@ -62,7 +61,7 @@ class AdvancePaymentRequest(models.Model): last_status = fields.Selection([ ('draft', 'Draft'), ('pengajuan1', 'Menunggu Approval Departement'), - ('pengajuan2', 'Menunggu Pengecekan AP'), + ('pengajuan2', 'Menunggu Approval AP'), ('pengajuan3', 'Menunggu Approval Pimpinan'), ('approved', 'Approved'), ('reject', 'Rejected') @@ -94,7 +93,8 @@ class AdvancePaymentRequest(models.Model): ('marketing', 'Marketing'), ('logistic', 'Logistic'), ('procurement', 'Procurement'), - ('fat_it', 'FAT & IT'), + ('fat', 'FAT'), + ('it', 'IT'), ('hr_ga', 'HR & GA'), ], string='Departement Type', tracking=3, required=True) @@ -295,7 +295,7 @@ class AdvancePaymentRequest(models.Model): # Sales & MD : Darren ID 19 # Marketing : Iwan ID 216 # Logistic & Procurement : Rafly H ID 21 - # FAT : Stephan ID 28 + # FAT & IT : Stephan ID 28 # HR & GA : Akbar ID 7 / Pimpinan # --------------------------------------- # AP : Manzila (Finance) ID 23 @@ -308,29 +308,46 @@ class AdvancePaymentRequest(models.Model): 'logistic': 21, 'procurement': 21, 'fat': 28, + 'it': 28, 'hr_ga': 7, } def _get_departement_approver(self): mapping = self._get_approver_mapping() return mapping.get(self.departement_type) + + @api.onchange('applicant_name') + def _onchange_applicant_name_set_position(self): + if self.applicant_name: + user_id = self.applicant_name.id + mapping = self._get_approver_mapping() + manager_ids = set(mapping.values()) + pimpinan_id = 7 + if user_id == pimpinan_id: + self.position_type = 'pimpinan' + elif user_id in manager_ids: + self.position_type = 'manager' + else: + self.position_type = 'staff' + else: + self.position_type = False - @api.model - def default_get(self, fields_list): - defaults = super(AdvancePaymentRequest, self).default_get(fields_list) - user_id = defaults.get('user_id', self.env.uid) - mapping = self._get_approver_mapping() - manager_ids = set(mapping.values()) - pimpinan_id = 7 + # @api.model + # def default_get(self, fields_list): + # defaults = super(AdvancePaymentRequest, self).default_get(fields_list) + # user_id = defaults.get('user_id', self.env.uid) + # mapping = self._get_approver_mapping() + # manager_ids = set(mapping.values()) + # pimpinan_id = 7 - position = 'staff' - if user_id == pimpinan_id: - position = 'pimpinan' - elif user_id in manager_ids: - position = 'manager' + # position = 'staff' + # if user_id == pimpinan_id: + # position = 'pimpinan' + # elif user_id in manager_ids: + # position = 'manager' - defaults['position_type'] = position - return defaults + # defaults['position_type'] = position + # return defaults def action_realisasi_pum(self): self.ensure_one() @@ -429,6 +446,7 @@ class AdvancePaymentRequest(models.Model): 'logistic': 'Logistic Manager', 'procurement': 'Procurement Manager', 'fat': 'Finance & Accounting Manager', + 'it': 'IT Manager', 'hr_ga': 'HR & GA Manager', } rec.position_department = department_titles.get(rec.departement_type, 'Departement Manager') @@ -746,7 +764,7 @@ class AdvancePaymentSettlement(models.Model): status = fields.Selection([ ('pengajuan1', 'Menunggu Approval Departement'), - ('pengajuan2', 'Menunggu Pengecekan AP'), + ('pengajuan2', 'Menunggu Approval AP'), ('pengajuan3', 'Menunggu Approval Pimpinan'), ('approved', 'Approved'), ], string='Status', default='pengajuan1', tracking=3, index=True, track_visibility='onchange') @@ -987,6 +1005,7 @@ class AdvancePaymentSettlement(models.Model): 'logistic': 'Logistic Manager', 'procurement': 'Procurement Manager', 'fat': 'Finance & Accounting Manager', + 'it': 'IT Manager', 'hr_ga': 'HR & GA Manager', } rec.position_department = department_titles.get(rec.pum_id.departement_type, 'Departement Manager') -- cgit v1.2.3 From 99896ae7999de433837faed09466c06845493770 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 10 Oct 2025 14:01:49 +0700 Subject: (andri) add reimburse line --- .../models/advance_payment_request.py | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 578829f2..412afa7a 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -133,6 +133,7 @@ class AdvancePaymentRequest(models.Model): ]) apr_perjalanan = fields.Boolean(string = "PUM Perjalanan?", default = False) + reimburse_line_ids = fields.One2many('reimburse.line', 'request_id', string='Rincian Reimburse') @api.onchange('nominal') def _onchange_nominal_no_minus(self): @@ -315,6 +316,11 @@ class AdvancePaymentRequest(models.Model): def _get_departement_approver(self): mapping = self._get_approver_mapping() return mapping.get(self.departement_type) + + @api.constrains('apr_perjalanan', 'date_back_to_office') + def _check_date_back_to_office(self): + if self.apr_perjalanan and not self.date_back_to_office: + raise ValidationError("Tanggal Kembali ke Kantor wajib diisi jika PUM Perjalanan dicentang.") @api.onchange('applicant_name') def _onchange_applicant_name_set_position(self): @@ -723,6 +729,31 @@ class AdvancePaymentUsageLine(models.Model): } } +class ReimburseLine(models.Model): + _name = 'reimburse.line' + _description = 'Reimburse Line' + + request_id = fields.Many2one('advance.payment.request', string='Request') + date = fields.Date(string='Tanggal', required=True, default=fields.Date.today) + account_id = fields.Many2one( + 'account.account', + string='Jenis Biaya', + required=True, + domain="[('id', 'in', [484, 486, 527, 529, 530, 471, 473, 492, 493, 488, 625, 528, 533, 534])]" + ) + description = fields.Char(string='Description', required=True) + distance = fields.Float(string='Jarak (km)') + quantity = fields.Float(string='Quantity') + price = fields.Float(string='Price') + total = fields.Float(string='Total') + currency_id = fields.Many2one(related='request_id.currency_id') + + is_vehicle = fields.Boolean(string='Berkendara?') + vehicle_type = fields.Selection([ + ('motor', 'Motor'), + ('car', 'Mobil'), + ], string='Tipe Kendaraan') + class AdvancePaymentSettlement(models.Model): _name = 'advance.payment.settlement' _description = 'Advance Payment Settlement' -- cgit v1.2.3 From 90a20c802a1675cf781c553de03fa1cb7ba6e2ac Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 10 Oct 2025 14:47:04 +0700 Subject: (andri)add attachment --- .../models/advance_payment_request.py | 25 ++++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 412afa7a..ae15a86f 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -738,21 +738,32 @@ class ReimburseLine(models.Model): account_id = fields.Many2one( 'account.account', string='Jenis Biaya', - required=True, domain="[('id', 'in', [484, 486, 527, 529, 530, 471, 473, 492, 493, 488, 625, 528, 533, 534])]" ) - description = fields.Char(string='Description', required=True) - distance = fields.Float(string='Jarak (km)') - quantity = fields.Float(string='Quantity') - price = fields.Float(string='Price') - total = fields.Float(string='Total') + description = fields.Char(string='Description', required=True, tracking=3) + distance = fields.Float(string='Jarak (km)', tracking=3) + quantity = fields.Float(string='Quantity', tracking=3) + price = fields.Float(string='Price', tracking=3) + total = fields.Float(string='Total', tracking=3) currency_id = fields.Many2one(related='request_id.currency_id') is_vehicle = fields.Boolean(string='Berkendara?') vehicle_type = fields.Selection([ ('motor', 'Motor'), ('car', 'Mobil'), - ], string='Tipe Kendaraan') + ], string='Tipe Kendaraan', tracking=3) + + attachment_image = fields.Binary(string='Image', attachment_filename='attachment_name_image') + attachment_pdf = fields.Binary(string='PDF', attachment_filename='attachment_name_pdf') + attachment_name_image = fields.Char(string='Filename Image') + attachment_name_pdf = fields.Char(string='Filename PDF') + + attachment_type = fields.Selection([ + ('pdf', 'PDF'), + ('image', 'Image'), + ], string="Attachment Type") + + is_checked = fields.Boolean(string='Checked', default=False) class AdvancePaymentSettlement(models.Model): _name = 'advance.payment.settlement' -- cgit v1.2.3 From 812bdbebbbe1cc0b1cc6294491f7771a668a04bd Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Sat, 11 Oct 2025 10:14:34 +0700 Subject: (andri) schema BBM --- indoteknik_custom/models/advance_payment_request.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index ae15a86f..fe82657b 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -765,6 +765,25 @@ class ReimburseLine(models.Model): is_checked = fields.Boolean(string='Checked', default=False) + @api.onchange('is_vehicle', 'vehicle_type', 'distance') + def _onchange_vehicle_data(self): + if not self.is_vehicle: + self.vehicle_type = False + self.distance = 0 + self.price = 0 + return + + if self.vehicle_type and self.distance > 0: + biaya_per_km = 0 + if self.vehicle_type == 'car': + biaya_per_km = 1000 # Rp 10.000 / 10 km + elif self.vehicle_type == 'motor': + biaya_per_km = 500 # Rp 10.000 / 20 km + self.price = self.distance * biaya_per_km + # self.quantity = 1 + else: + self.price = 0 + class AdvancePaymentSettlement(models.Model): _name = 'advance.payment.settlement' _description = 'Advance Payment Settlement' -- cgit v1.2.3 From 698650e73503d0b780694e150fb5cb3f8e91d83d Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 13 Oct 2025 10:53:11 +0700 Subject: (andri) add seq reimburse, penyesuaian approve, & add diwakilkan --- .../models/advance_payment_request.py | 69 +++++++++++++++++----- 1 file changed, 54 insertions(+), 15 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index fe82657b..dbf2e9ee 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -21,12 +21,12 @@ class AdvancePaymentRequest(models.Model): number = fields.Char(string='No. Dokumen', default='New Draft', tracking=3) - applicant_name = fields.Many2one('res.users', string='Nama Pemohon', required=True, tracking=3, domain="[('groups_id', 'in', [1])]") + applicant_name = fields.Many2one('res.users', string='Nama Pemohon', default=lambda self: self.env.user, required=True, tracking=3, domain="[('groups_id', 'in', [1])]") # applicant_name = fields.One2many(string='Nama Pemohon', related='res.users') nominal = fields.Float(string='Nominal', tracking=3, required=True) bank_name = fields.Char(string='Bank', tracking=3, required=True) - account_name = fields.Many2one('res.users', string='Nama Account', required=True, tracking=3, domain="[('groups_id', 'in', [1])]") + account_name = fields.Many2one('res.users', string='Nama Account', default=lambda self: self.env.user, required=True, tracking=3, domain="[('groups_id', 'in', [1])]") # 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) @@ -120,7 +120,7 @@ class AdvancePaymentRequest(models.Model): type_request = fields.Selection([ ('pum', 'PUM'), - ('reimburse', 'Reimburse')], string='Tipe Pengajuan', default='pum', tracking=3) + ('reimburse', 'Reimburse')], string='Tipe Pengajuan', tracking=3) position_type = fields.Selection([ ('staff', 'Staff'), @@ -132,9 +132,20 @@ class AdvancePaymentRequest(models.Model): ('settlement', 'Realisasi') ]) + is_represented = fields.Boolean(string='Nama Pemohon Berbeda?', default=False) + apr_perjalanan = fields.Boolean(string = "PUM Perjalanan?", default = False) reimburse_line_ids = fields.One2many('reimburse.line', 'request_id', string='Rincian Reimburse') + @api.onchange('is_represented') + def _onchange_is_represented(self): + if self.is_represented: + self.account_name = False + self.applicant_name = False + else: + self.account_name = self.env.user.id + self.applicant_name = self.env.user.id + @api.onchange('nominal') def _onchange_nominal_no_minus(self): if self.nominal and self.nominal < 0: @@ -473,7 +484,10 @@ class AdvancePaymentRequest(models.Model): rec.email_ap = self.env.user.email rec.date_approved_ap = now rec.position_ap = 'Finance AP' - rec.status = 'pengajuan3' + if rec.position_type == 'pimpinan': + rec.status = 'approved' + else: + rec.status = 'pengajuan3' rec.message_post( body=f"Approval AP oleh {self.env.user.name} " @@ -561,23 +575,29 @@ class AdvancePaymentRequest(models.Model): } - @api.depends('date_back_to_office', 'status') + @api.depends('date_back_to_office', 'status', 'apr_perjalanan', 'create_date') def _compute_days_remaining(self): today = date.today() for rec in self: if rec.status in ['approved', 'reject'] and rec.days_remaining: continue - if rec.date_back_to_office: - due_date = rec.date_back_to_office + timedelta(days=7) + if rec.apr_perjalanan: + 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 + else: + base_date = rec.create_date.date() if rec.create_date else today + due_date = base_date + 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 + rec.days_remaining = (due_date - base_date).days @api.onchange('date_back_to_office') def _onchange_date_back_to_office(self): @@ -601,6 +621,8 @@ class AdvancePaymentRequest(models.Model): @api.onchange('user_id') def _onchange_user_id_limit_check(self): + if self.type_request != 'pum': + return if not self.user_id: return @@ -641,8 +663,25 @@ class AdvancePaymentRequest(models.Model): 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('apr_perjalanan'): + if 'estimated_return_date' not in vals: + today = date.today() + due_date = today + timedelta(days=7) + vals['estimated_return_date'] = due_date + + initial_status = '' + position = vals.get('position_type') + if position == 'staff': + initial_status = 'pengajuan1' + elif position in ('manager', 'pimpinan'): + initial_status = 'pengajuan2' + vals['status'] = initial_status + if not vals.get('number') or vals['number'] == 'New Draft': - vals['number'] = self.env['ir.sequence'].next_by_code('advance.payment.request') or 'New Draft' + if vals.get('type_request') == 'reimburse': + vals['number'] = self.env['ir.sequence'].next_by_code('reimburse.request') or 'New Draft' + else: + vals['number'] = self.env['ir.sequence'].next_by_code('advance.payment.request') or 'New Draft' vals['status'] = 'pengajuan1' return super(AdvancePaymentRequest, self).create(vals) -- cgit v1.2.3 From 99d097d93635eaa1cf92064f1e9070107fe0d8a5 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 13 Oct 2025 11:07:42 +0700 Subject: (andri) checked & jenis biaya hanya bisa AP --- indoteknik_custom/models/advance_payment_request.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index dbf2e9ee..02b1a23f 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -804,6 +804,18 @@ class ReimburseLine(models.Model): is_checked = fields.Boolean(string='Checked', default=False) + is_current_user_ap = fields.Boolean( + string="Is Current User AP", + compute='_compute_is_current_user_ap' + ) + + def _compute_is_current_user_ap(self): + ap_user_ids = [23, 9468] + is_ap = self.env.user.id in ap_user_ids + for line in self: + line.is_current_user_ap = is_ap + + @api.onchange('is_vehicle', 'vehicle_type', 'distance') def _onchange_vehicle_data(self): if not self.is_vehicle: -- cgit v1.2.3 From 1de6ff22b0c3177d01d989b2c4fab1c64c45c1cf Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 13 Oct 2025 14:31:22 +0700 Subject: (andri) fix --- .../models/advance_payment_request.py | 38 +++++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 02b1a23f..5e8bd88e 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -12,7 +12,7 @@ import base64 class AdvancePaymentRequest(models.Model): _name = 'advance.payment.request' - _description = 'Advance Payment Request & Reimburse' + _description = 'Advance Payment Request or Reimburse' _rec_name = 'number' _inherit = ['mail.thread', 'mail.activity.mixin'] @@ -476,9 +476,9 @@ class AdvancePaymentRequest(models.Model): ) 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.") + # 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap rec.email_ap = self.env.user.email @@ -660,7 +660,7 @@ class AdvancePaymentRequest(models.Model): if not realization or realization.done_status != 'done_not_realized': active_pum_count += 1 - if active_pum_count >= 2: + if active_pum_count >= 2 and vals.get('type_request') == 'pum': raise UserError("Anda hanya dapat mengajukan maksimal 2 PUM aktif. Silakan realisasikan salah satunya terlebih dahulu.") if not vals.get('apr_perjalanan'): @@ -683,7 +683,7 @@ class AdvancePaymentRequest(models.Model): else: vals['number'] = self.env['ir.sequence'].next_by_code('advance.payment.request') or 'New Draft' - vals['status'] = 'pengajuan1' + # vals['status'] = 'pengajuan1' return super(AdvancePaymentRequest, self).create(vals) @@ -692,9 +692,9 @@ class AdvancePaymentSettlementLine(models.Model): _description = 'Advance Payment Settlement Line' realization_id = fields.Many2one('advance.payment.settlement', 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) + date = fields.Date(string='Tanggal', default=fields.Date.today) + description = fields.Char(string='Description') + value = fields.Float(string='Nilai') class AdvancePaymentUsageLine(models.Model): @@ -728,6 +728,17 @@ class AdvancePaymentUsageLine(models.Model): domain="[('id', 'in', [484, 486, 488, 506, 507, 625, 471, 519, 527, 528, 529, 530, 565])]" # ID Jenis Biaya yang dibutuhkan ) + is_current_user_ap = fields.Boolean( + string="Is Current User AP", + compute='_compute_is_current_user_ap' + ) + + def _compute_is_current_user_ap(self): + ap_user_ids = [23, 9468] + is_ap = self.env.user.id in ap_user_ids + for line in self: + line.is_current_user_ap = is_ap + @api.onchange('account_id') def _onchange_account_id(self): for rec in self: @@ -1175,6 +1186,15 @@ class AdvancePaymentSettlement(models.Model): @api.model def create(self, vals): + pum_id = vals.get('pum_id') + if pum_id: + pum_request = self.env['advance.payment.request'].browse(pum_id) + position = pum_request.position_type + if position == 'staff': + vals['status'] = 'pengajuan1' + elif position in ('manager', 'pimpinan'): + vals['status'] = 'pengajuan2' + rec = super().create(vals) rec._check_remaining_value() return rec -- cgit v1.2.3 From 545d32974a0d18a4a6560ad222ca5ab7cd0552f6 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 14 Oct 2025 13:23:26 +0700 Subject: (andri) add realisasi di oe button, revisi make CAB, add date upload bukti tf --- .../models/advance_payment_request.py | 64 ++++++++++++++++------ 1 file changed, 48 insertions(+), 16 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 5e8bd88e..c511f2e7 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -43,7 +43,7 @@ class AdvancePaymentRequest(models.Model): ) days_remaining = fields.Integer( - string='Sisa Hari Pengajuan', + string='Sisa Hari Realisasi', compute='_compute_days_remaining', help='Sisa hari batas maksimal pengajuan realisasi setelah kembali ke kantor. ' '7 hari setelah tanggal kembali.' @@ -136,6 +136,33 @@ class AdvancePaymentRequest(models.Model): apr_perjalanan = fields.Boolean(string = "PUM Perjalanan?", default = False) reimburse_line_ids = fields.One2many('reimburse.line', 'request_id', string='Rincian Reimburse') + upload_attachment_date = fields.Datetime(string='Upload Attachment Date', tracking=3) + settlement_ids = fields.One2many( + 'advance.payment.settlement', + 'pum_id', + string='Realisasi' + ) + has_settlement = fields.Boolean( + string='Has Settlement', + compute='_compute_has_settlement' + ) + settlement_name = fields.Char( + string="Nama Realisasi", + compute='_compute_settlement_name' + ) + + @api.depends('settlement_ids') + def _compute_has_settlement(self): + for rec in self: + rec.has_settlement = bool(rec.settlement_ids) + + @api.depends('settlement_ids', 'settlement_ids.name') + def _compute_settlement_name(self): + for request in self: + if request.settlement_ids: + request.settlement_name = request.settlement_ids[0].name + else: + request.settlement_name = False @api.onchange('is_represented') def _onchange_is_represented(self): @@ -281,9 +308,9 @@ class AdvancePaymentRequest(models.Model): 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.') + # 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.") @@ -403,6 +430,9 @@ class AdvancePaymentRequest(models.Model): def action_confirm_payment(self): + jakarta_tz = pytz.timezone('Asia/Jakarta') + now = datetime.now(jakarta_tz).replace(tzinfo=None) + 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.') @@ -415,6 +445,7 @@ class AdvancePaymentRequest(models.Model): ) rec.status_pay_down_payment = 'payment' + rec.upload_attachment_date = now rec.message_post( body="Status pembayaran telah dikonfirmasi oleh AP.", @@ -555,9 +586,9 @@ class AdvancePaymentRequest(models.Model): 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.') + # 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.') @@ -570,7 +601,7 @@ class AdvancePaymentRequest(models.Model): 'target': 'new', 'context': { 'default_nominal': self.nominal, - 'default_down_payment_id': self.id, + 'default_apr_id': self.id, } } @@ -850,6 +881,7 @@ class AdvancePaymentSettlement(models.Model): _name = 'advance.payment.settlement' _description = 'Advance Payment Settlement' _inherit = ['mail.thread'] + _rec_name = 'name' pum_id = fields.Many2one('advance.payment.request', string='No PUM') name = fields.Char(string='Nama', readonly=True, tracking=3) @@ -1224,12 +1256,12 @@ class AdvancePaymentCreateBill(models.TransientModel): _name = 'advance.payment.create.bill' _description = 'Create Bill from Advance Payment' - down_payment_id = fields.Many2one('advance.payment.request', string='Down Payment', required=True) + apr_id = fields.Many2one('advance.payment.request', string='Advance Payment Request', 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') + nominal = fields.Float(string='Nominal', related='apr_id.nominal') def action_create_cab(self): self.ensure_one() @@ -1237,10 +1269,10 @@ class AdvancePaymentCreateBill(models.TransientModel): # 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 + apr = self.apr_id + partner_id = apr.user_id.partner_id.id - ref_label = f'{dp.number} - Biaya {dp.detail_note or "-"}' + ref_label = f'{apr.number} - Biaya {apr.detail_note or "-"}' move = self.env['account.move'].create({ 'ref': ref_label, @@ -1251,7 +1283,7 @@ class AdvancePaymentCreateBill(models.TransientModel): 'account_id': 403, # Uang Muka Operasional 'partner_id': partner_id, 'name': ref_label, - 'debit': dp.nominal, + 'debit': apr.nominal, 'credit': 0, }), (0, 0, { @@ -1259,12 +1291,12 @@ class AdvancePaymentCreateBill(models.TransientModel): 'partner_id': partner_id, 'name': ref_label, 'debit': 0, - 'credit': dp.nominal, + 'credit': apr.nominal, }) ] }) - dp.move_id = move.id # jika ada field untuk menampung move_id + apr.move_id = move.id # jika ada field untuk menampung move_id return { 'name': _('Journal Entry'), -- cgit v1.2.3 From b0bde0e002f5321f7462f520d545876cdc14fd2e Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 14 Oct 2025 15:22:46 +0700 Subject: (andri) add cab reimburse + fix view realisasi --- .../models/advance_payment_request.py | 143 +++++++++++++++++++-- 1 file changed, 130 insertions(+), 13 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index c511f2e7..5c6d59c4 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -72,18 +72,18 @@ class AdvancePaymentRequest(models.Model): ('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) + name_approval_departement = fields.Char(string='Approval Departement') + name_approval_ap = fields.Char(string='Approval AP') email_ap = fields.Char(string = 'Email AP') - name_approval_pimpinan = fields.Char(string='Approval Pimpinan', tracking=True) + name_approval_pimpinan = fields.Char(string='Approval Pimpinan') 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) + position_department = fields.Char(string='Position Departement') + position_ap = fields.Char(string='Position AP') + position_pimpinan = fields.Char(string='Position Pimpinan') approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') @@ -151,6 +151,29 @@ class AdvancePaymentRequest(models.Model): compute='_compute_settlement_name' ) + total_reimburse = fields.Monetary( + string='Total Reimburse', + compute='_compute_total_reimburse' + ) + + @api.depends('reimburse_line_ids.total') + def _compute_total_reimburse(self): + for request in self: + request.total_reimburse = sum(request.reimburse_line_ids.mapped('total')) + + def action_open_create_reimburse_cab(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': 'Buat Jurnal Reimburse', + 'res_model': 'create.reimburse.cab.wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_request_id': self.id, + } + } + @api.depends('settlement_ids') def _compute_has_settlement(self): for rec in self: @@ -903,19 +926,19 @@ class AdvancePaymentSettlement(models.Model): 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) + name_approval_departement = fields.Char(string='Approval Departement') + name_approval_ap = fields.Char(string='Approval AP') + name_approval_pimpinan = fields.Char(string='Approval Pimpinan') 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) + position_department = fields.Char(string='Position Departement') + position_ap = fields.Char(string='Position AP') + position_pimpinan = fields.Char(string='Position Pimpinan') - approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') + approved_by = fields.Char(string='Approved By', track_visibility='always') status = fields.Selection([ ('pengajuan1', 'Menunggu Approval Departement'), @@ -955,6 +978,19 @@ class AdvancePaymentSettlement(models.Model): 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') + user_id = fields.Many2one( + 'res.users', + string='Diajukan Oleh', + related='pum_id.user_id', + readonly=True + ) + applicant_name = fields.Many2one( + 'res.users', + string='Nama Pemohon', + related='pum_id.applicant_name', + readonly=True + ) + def action_toggle_check_attachment(self): ap_user_ids = [23, 9468] if self.env.user.id not in ap_user_ids: @@ -1298,6 +1334,87 @@ class AdvancePaymentCreateBill(models.TransientModel): apr.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', + } + +class CreateReimburseCabWizard(models.TransientModel): + _name = 'create.reimburse.cab.wizard' + _description = 'Wizard untuk Membuat Jurnal Reimburse' + + # Field untuk menampung ID request yang sedang diproses + request_id = fields.Many2one('advance.payment.request', string='Pengajuan', readonly=True) + + # Field untuk memilih salah satu dari dua bank Anda + account_id = fields.Many2one( + 'account.account', + string='Bank Intransit (Credit)', + required=True, + # Domain untuk membatasi pilihan hanya pada ID 573 dan 389 + domain="[('id', 'in', [573, 389])]" + ) + + # Field untuk menampilkan total agar pengguna bisa konfirmasi + total_reimburse = fields.Monetary( + string='Total Reimburse', + related='request_id.total_reimburse', + readonly=True + ) + currency_id = fields.Many2one(related='request_id.currency_id', readonly=True) + + def action_create_reimburse_cab(self): + """Metode ini yang akan membuat Journal Entry (CAB).""" + self.ensure_one() + request = self.request_id + + # --- Validasi --- + if request.move_id: + raise UserError("Jurnal sudah pernah dibuat untuk pengajuan ini.") + if not request.reimburse_line_ids: + raise UserError("Tidak ada rincian reimburse yang bisa dijurnalkan.") + + lines = [] + partner_id = request.user_id.partner_id.id + + # 1. Buat Jurnal DEBIT dari setiap baris reimburse + for line in request.reimburse_line_ids: + if not line.account_id: + raise UserError(f"Jenis Biaya pada baris '{line.description}' belum diisi oleh AP.") + + lines.append((0, 0, { + 'account_id': line.account_id.id, + 'partner_id': partner_id, + 'name': line.description, + 'debit': line.total, + 'credit': 0, + })) + + # 2. Buat satu Jurnal CREDIT ke bank yang dipilih di wizard + lines.append((0, 0, { + 'account_id': self.account_id.id, + 'partner_id': partner_id, + 'name': f'Reimburse {request.number}', + 'debit': 0, + 'credit': request.total_reimburse, + })) + + # 3. Buat Journal Entry + move = self.env['account.move'].create({ + 'ref': request.number, + 'date': fields.Date.context_today(self), + 'journal_id': 11, # PENTING: Ganti 11 dengan ID Journal "Miscellaneous" Anda + 'line_ids': lines, + }) + + # 4. Tautkan journal yang baru dibuat ke request + request.move_id = move.id + + # 5. Buka tampilan form journal yang baru dibuat return { 'name': _('Journal Entry'), 'view_mode': 'form', -- cgit v1.2.3 From f505e2b3aec7ad467fc9e7ecb29a6c37a1f6e10c Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 14 Oct 2025 19:06:08 +0700 Subject: q ' --- .../models/advance_payment_request.py | 50 +++++++++++----------- 1 file changed, 24 insertions(+), 26 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 5c6d59c4..77e78850 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -441,13 +441,13 @@ class AdvancePaymentRequest(models.Model): '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 - }) - ] + # '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 + # }) + # ] } } @@ -741,14 +741,14 @@ class AdvancePaymentRequest(models.Model): return super(AdvancePaymentRequest, self).create(vals) -class AdvancePaymentSettlementLine(models.Model): - _name = 'advance.payment.settlement.line' - _description = 'Advance Payment Settlement Line' +# class AdvancePaymentSettlementLine(models.Model): +# _name = 'advance.payment.settlement.line' +# _description = 'Advance Payment Settlement Line' - realization_id = fields.Many2one('advance.payment.settlement', string='Realization') - date = fields.Date(string='Tanggal', default=fields.Date.today) - description = fields.Char(string='Description') - value = fields.Float(string='Nilai') +# realization_id = fields.Many2one('advance.payment.settlement', string='Realization') +# date = fields.Date(string='Tanggal', default=fields.Date.today) +# description = fields.Char(string='Description') +# value = fields.Float(string='Nilai') class AdvancePaymentUsageLine(models.Model): @@ -896,7 +896,7 @@ class ReimburseLine(models.Model): elif self.vehicle_type == 'motor': biaya_per_km = 500 # Rp 10.000 / 20 km self.price = self.distance * biaya_per_km - # self.quantity = 1 + self.quantity = self.distance else: self.price = 0 @@ -912,14 +912,14 @@ class AdvancePaymentSettlement(models.Model): goals = fields.Text(string='Tujuan', tracking=3) related = fields.Char(string='Terkait', tracking=3) - pemberian_line_ids = fields.One2many( - 'advance.payment.settlement.line', 'realization_id', string='Rincian Pemberian' - ) + # pemberian_line_ids = fields.One2many( + # 'advance.payment.settlement.line', 'realization_id', string='Rincian Pemberian' + # ) penggunaan_line_ids = fields.One2many( 'advance.payment.usage.line', 'realization_id', string='Rincian Penggunaan' ) - grand_total = fields.Float(string='Grand Total Pemberian', tracking=3, compute='_compute_grand_total') + # 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') @@ -1050,20 +1050,18 @@ class AdvancePaymentSettlement(models.Model): 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('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 + return def action_validation(self): self.ensure_one() -- cgit v1.2.3 From d893f19a3a3d4bbfe073a55459ac9f1253736016 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 14 Oct 2025 19:30:16 +0700 Subject: (andri) hapus yang tidak dibutuhkan --- .../models/advance_payment_request.py | 110 ++++++++++----------- 1 file changed, 50 insertions(+), 60 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 77e78850..f2c0b58e 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -55,17 +55,8 @@ class AdvancePaymentRequest(models.Model): ('pengajuan2', 'Menunggu Approval 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 Approval AP'), - ('pengajuan3', 'Menunggu Approval Pimpinan'), - ('approved', 'Approved'), - ('reject', 'Rejected') - ], string='Status') status_pay_down_payment = fields.Selection([ ('pending', 'Pending'), @@ -111,7 +102,6 @@ class AdvancePaymentRequest(models.Model): 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') currency_id = fields.Many2one( 'res.currency', string='Currency', @@ -568,42 +558,42 @@ class AdvancePaymentRequest(models.Model): # 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': 'advance.payment.request.reject', - 'view_mode': 'form', - 'target': 'new', - 'context': {'default_request_id': self.id}, - } + # def action_reject(self): + # return { + # 'type': 'ir.actions.act_window', + # 'name': 'Alasan Penolakan', + # 'res_model': 'advance.payment.request.reject', + # '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_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): @@ -1270,21 +1260,21 @@ class AdvancePaymentSettlement(models.Model): self._check_remaining_value() return res -class AdvancePaymentRequestReject(models.TransientModel): - _name = 'advance.payment.request.reject' - _description = 'Wizard for Reject Reason APR' - - request_id = fields.Many2one('advance.payment.request', 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 AdvancePaymentRequestReject(models.TransientModel): +# _name = 'advance.payment.request.reject' +# _description = 'Wizard for Reject Reason APR' + +# request_id = fields.Many2one('advance.payment.request', 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 AdvancePaymentCreateBill(models.TransientModel): _name = 'advance.payment.create.bill' -- cgit v1.2.3 From ef67567976a24d59fe8859b55480e4053b64f65f Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 14 Oct 2025 19:31:53 +0700 Subject: (andri) del --- .../models/advance_payment_request.py | 62 ---------------------- 1 file changed, 62 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index f2c0b58e..eba01550 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -558,43 +558,6 @@ class AdvancePaymentRequest(models.Model): # 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': 'advance.payment.request.reject', - # '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() @@ -731,16 +694,6 @@ class AdvancePaymentRequest(models.Model): return super(AdvancePaymentRequest, self).create(vals) -# class AdvancePaymentSettlementLine(models.Model): -# _name = 'advance.payment.settlement.line' -# _description = 'Advance Payment Settlement Line' - -# realization_id = fields.Many2one('advance.payment.settlement', string='Realization') -# date = fields.Date(string='Tanggal', default=fields.Date.today) -# description = fields.Char(string='Description') -# value = fields.Float(string='Nilai') - - class AdvancePaymentUsageLine(models.Model): _name = 'advance.payment.usage.line' _description = 'Advance Payment Usage Line' @@ -1260,21 +1213,6 @@ class AdvancePaymentSettlement(models.Model): self._check_remaining_value() return res -# class AdvancePaymentRequestReject(models.TransientModel): -# _name = 'advance.payment.request.reject' -# _description = 'Wizard for Reject Reason APR' - -# request_id = fields.Many2one('advance.payment.request', 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 AdvancePaymentCreateBill(models.TransientModel): _name = 'advance.payment.create.bill' -- cgit v1.2.3 From a4363a5468a5a9d6a3815cb8ae304fe1da3aef30 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Sat, 18 Oct 2025 15:26:50 +0700 Subject: (andri) fix field + readonly action view journal --- .../models/advance_payment_request.py | 35 ++++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index eba01550..ad97755a 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -146,6 +146,17 @@ class AdvancePaymentRequest(models.Model): compute='_compute_total_reimburse' ) + is_current_user_ap = fields.Boolean( + string="Is Current User AP", + compute='_compute_is_current_user_ap' + ) + + def _compute_is_current_user_ap(self): + ap_user_ids = [23, 9468] + is_ap = self.env.user.id in ap_user_ids + for line in self: + line.is_current_user_ap = is_ap + @api.depends('reimburse_line_ids.total') def _compute_total_reimburse(self): for request in self: @@ -429,7 +440,7 @@ class AdvancePaymentRequest(models.Model): 'target': 'current', 'context': { 'default_pum_id': self.id, - 'default_value_down_payment': self.nominal, + # 'default_value_down_payment': self.nominal, 'default_name': f'Realisasi - {self.number or ""}', # 'default_pemberian_line_ids': [ # (0, 0, { @@ -849,7 +860,7 @@ class AdvancePaymentSettlement(models.Model): _inherit = ['mail.thread'] _rec_name = 'name' - pum_id = fields.Many2one('advance.payment.request', string='No PUM') + pum_id = fields.Many2one('advance.payment.request', string='No PUM', ondelete='cascade') name = fields.Char(string='Nama', readonly=True, tracking=3) title = fields.Char(string='Judul', tracking=3) goals = fields.Text(string='Tujuan', tracking=3) @@ -862,9 +873,14 @@ class AdvancePaymentSettlement(models.Model): 'advance.payment.usage.line', 'realization_id', string='Rincian Penggunaan' ) + nominal_pum = fields.Float( + string='Nominal Pemberian PUM', + related='pum_id.nominal', + readonly=True ) + # 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) + # 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) @@ -933,6 +949,16 @@ class AdvancePaymentSettlement(models.Model): related='pum_id.applicant_name', readonly=True ) + is_current_user_ap = fields.Boolean( + string="Is Current User AP", + compute='_compute_is_current_user_ap' + ) + + def _compute_is_current_user_ap(self): + ap_user_ids = [23, 9468] + is_ap = self.env.user.id in ap_user_ids + for line in self: + line.is_current_user_ap = is_ap def action_toggle_check_attachment(self): ap_user_ids = [23, 9468] @@ -1003,7 +1029,10 @@ class AdvancePaymentSettlement(models.Model): for rec in self: rec.grand_total_use = sum(line.nominal for line in rec.penggunaan_line_ids) + @api.depends('nominal_pum', 'grand_total_use') def _compute_remaining_value(self): + for rec in self: + rec.remaining_value = rec.nominal_pum - rec.grand_total_use return def action_validation(self): -- cgit v1.2.3 From 06e514d5287d2a316787e80648a06b2d92dc0d47 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 20 Oct 2025 10:26:32 +0700 Subject: (andri) grandtotal reimburse --- .../models/advance_payment_request.py | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index ad97755a..56d4d24d 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -141,9 +141,10 @@ class AdvancePaymentRequest(models.Model): compute='_compute_settlement_name' ) - total_reimburse = fields.Monetary( + grand_total_reimburse = fields.Monetary( string='Total Reimburse', - compute='_compute_total_reimburse' + compute='_compute_grand_total_reimburse', + currency_field='currency_id' ) is_current_user_ap = fields.Boolean( @@ -158,9 +159,9 @@ class AdvancePaymentRequest(models.Model): line.is_current_user_ap = is_ap @api.depends('reimburse_line_ids.total') - def _compute_total_reimburse(self): + def _compute_grand_total_reimburse(self): for request in self: - request.total_reimburse = sum(request.reimburse_line_ids.mapped('total')) + request.grand_total_reimburse = sum(request.reimburse_line_ids.mapped('total')) def action_open_create_reimburse_cab(self): self.ensure_one() @@ -799,10 +800,12 @@ class ReimburseLine(models.Model): domain="[('id', 'in', [484, 486, 527, 529, 530, 471, 473, 492, 493, 488, 625, 528, 533, 534])]" ) description = fields.Char(string='Description', required=True, tracking=3) - distance = fields.Float(string='Jarak (km)', tracking=3) + distance_departure = fields.Float(string='Pergi (Km)', tracking=3) + distance_return = fields.Float(string='Pulang (Km)', tracking=3) quantity = fields.Float(string='Quantity', tracking=3) price = fields.Float(string='Price', tracking=3) - total = fields.Float(string='Total', tracking=3) + total = fields.Float(string='Total', tracking=3, compute='_compute_total') + # total = fields.Float(string='Total', tracking=3) currency_id = fields.Many2one(related='request_id.currency_id') is_vehicle = fields.Boolean(string='Berkendara?') @@ -834,25 +837,34 @@ class ReimburseLine(models.Model): for line in self: line.is_current_user_ap = is_ap + @api.depends('quantity', 'price', 'is_vehicle') + def _compute_total(self): + for line in self: + line.total = line.quantity * line.price - @api.onchange('is_vehicle', 'vehicle_type', 'distance') + @api.onchange('is_vehicle', 'vehicle_type', 'distance_departure', 'distance_return') def _onchange_vehicle_data(self): if not self.is_vehicle: self.vehicle_type = False - self.distance = 0 + self.distance_departure = 0 + self.distance_return = 0 self.price = 0 return + + total_distance = self.distance_departure + self.distance_return - if self.vehicle_type and self.distance > 0: + if self.vehicle_type and total_distance > 0: biaya_per_km = 0 if self.vehicle_type == 'car': biaya_per_km = 1000 # Rp 10.000 / 10 km + self.price = biaya_per_km elif self.vehicle_type == 'motor': biaya_per_km = 500 # Rp 10.000 / 20 km - self.price = self.distance * biaya_per_km - self.quantity = self.distance + self.price = biaya_per_km + self.total = total_distance * biaya_per_km + self.quantity = total_distance else: - self.price = 0 + self.total = 0 class AdvancePaymentSettlement(models.Model): _name = 'advance.payment.settlement' @@ -1218,7 +1230,7 @@ class AdvancePaymentSettlement(models.Model): # Cek sisa PUM if rec.remaining_value < 0: raise ValidationError( - "Sisa uang PUM tidak boleh kurang dari 0.\n" + "Sisa uang PUM tidak boleh kurang dari Rp 0.\n" "Jika ada penggunaan uang pribadi, maka ajukan dengan sistem reimburse." ) @@ -1317,7 +1329,7 @@ class CreateReimburseCabWizard(models.TransientModel): # Field untuk menampilkan total agar pengguna bisa konfirmasi total_reimburse = fields.Monetary( string='Total Reimburse', - related='request_id.total_reimburse', + related='request_id.grand_total_reimburse', readonly=True ) currency_id = fields.Many2one(related='request_id.currency_id', readonly=True) -- cgit v1.2.3 From 4777ebcb87e53b2460661019d70c08aea938dd21 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 20 Oct 2025 11:19:03 +0700 Subject: (andri) fix create --- .../models/advance_payment_request.py | 41 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 56d4d24d..d1011e8e 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -664,8 +664,22 @@ class AdvancePaymentRequest(models.Model): } } + def _get_department_titles_mapping(self): + return { + 'sales': 'Sales Manager', + 'merchandiser': 'Merchandiser Manager', + 'marketing': 'Marketing Manager', + 'logistic': 'Logistic Manager', + 'procurement': 'Procurement Manager', + 'fat': 'Finance & Accounting Manager', + 'it': 'IT Manager', + 'hr_ga': 'HR & GA Manager', + } + @api.model def create(self, vals): + jakarta_tz = pytz.timezone('Asia/Jakarta') + now = datetime.now(jakarta_tz).replace(tzinfo=None) user = self.env.user pum_ids = self.search([ @@ -692,8 +706,21 @@ class AdvancePaymentRequest(models.Model): position = vals.get('position_type') if position == 'staff': initial_status = 'pengajuan1' - elif position in ('manager', 'pimpinan'): + elif position == 'manager': + initial_status = 'pengajuan2' + applicant_name = vals.get('applicant_name') + vals['name_approval_departement'] = self.env['res.users'].browse(applicant_name).name or '' + vals['date_approved_department'] = now + department_type = vals.get('departement_type') + department_titles = self._get_department_titles_mapping() + vals['position_department'] = department_titles.get(department_type, 'Departement Manager') + elif position == 'pimpinan': initial_status = 'pengajuan2' + applicant_name = vals.get('applicant_name') + vals['name_approval_pimpinan'] = self.env['res.users'].browse(applicant_name).name or '' + vals['position_pimpinan'] = 'Pimpinan' + vals['date_approved_pimpinan'] = now + vals['status'] = initial_status if not vals.get('number') or vals['number'] == 'New Draft': @@ -803,7 +830,7 @@ class ReimburseLine(models.Model): distance_departure = fields.Float(string='Pergi (Km)', tracking=3) distance_return = fields.Float(string='Pulang (Km)', tracking=3) quantity = fields.Float(string='Quantity', tracking=3) - price = fields.Float(string='Price', tracking=3) + price_unit = fields.Float(string='Price', tracking=3) total = fields.Float(string='Total', tracking=3, compute='_compute_total') # total = fields.Float(string='Total', tracking=3) currency_id = fields.Many2one(related='request_id.currency_id') @@ -837,10 +864,10 @@ class ReimburseLine(models.Model): for line in self: line.is_current_user_ap = is_ap - @api.depends('quantity', 'price', 'is_vehicle') + @api.depends('quantity', 'price_unit', 'is_vehicle') def _compute_total(self): for line in self: - line.total = line.quantity * line.price + line.total = line.quantity * line.price_unit @api.onchange('is_vehicle', 'vehicle_type', 'distance_departure', 'distance_return') def _onchange_vehicle_data(self): @@ -848,7 +875,7 @@ class ReimburseLine(models.Model): self.vehicle_type = False self.distance_departure = 0 self.distance_return = 0 - self.price = 0 + self.price_unit = 0 return total_distance = self.distance_departure + self.distance_return @@ -857,10 +884,10 @@ class ReimburseLine(models.Model): biaya_per_km = 0 if self.vehicle_type == 'car': biaya_per_km = 1000 # Rp 10.000 / 10 km - self.price = biaya_per_km + self.price_unit = biaya_per_km elif self.vehicle_type == 'motor': biaya_per_km = 500 # Rp 10.000 / 20 km - self.price = biaya_per_km + self.price_unit = biaya_per_km self.total = total_distance * biaya_per_km self.quantity = total_distance else: -- cgit v1.2.3 From ee41e453f3d7b1042ac0af4fbf51741503308781 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 20 Oct 2025 11:57:53 +0700 Subject: (andri) fix create realisasi --- .../models/advance_payment_request.py | 30 +++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index d1011e8e..cc3e57d6 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -1263,15 +1263,33 @@ class AdvancePaymentSettlement(models.Model): @api.model def create(self, vals): + jakarta_tz = pytz.timezone('Asia/Jakarta') + now = datetime.now(jakarta_tz).replace(tzinfo=None) pum_id = vals.get('pum_id') + initial_status = '' if pum_id: pum_request = self.env['advance.payment.request'].browse(pum_id) - position = pum_request.position_type - if position == 'staff': - vals['status'] = 'pengajuan1' - elif position in ('manager', 'pimpinan'): - vals['status'] = 'pengajuan2' - + if pum_request: + position_dari_pum = pum_request.position_type + if position_dari_pum == 'staff': + initial_status = 'pengajuan1' + elif position_dari_pum == 'manager': + initial_status = 'pengajuan2' + applicant_name_str = pum_request.applicant_name.name or '' + department_type = pum_request.departement_type + department_titles = pum_request._get_department_titles_mapping() + dept_position = department_titles.get(department_type, 'Departement Manager') + vals['date_approved_department'] = now + vals['name_approval_departement'] = applicant_name_str + vals['position_department'] = dept_position + elif position_dari_pum == 'pimpinan': + initial_status = 'pengajuan2' + applicant_name_str = pum_request.applicant_name.name or '' + vals['date_approved_pimpinan'] = now + vals['name_approval_pimpinan'] = applicant_name_str + vals['position_department'] = 'Pimpinan' + + vals['status'] = initial_status rec = super().create(vals) rec._check_remaining_value() return rec -- cgit v1.2.3 From fdf9425e60b8c91ef797f7c7b88baf98f2bd4619 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 20 Oct 2025 19:44:18 +0700 Subject: balikin validasi CCM --- indoteknik_custom/models/tukar_guling.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index cfa0ac77..cb630a04 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -492,14 +492,14 @@ class TukarGuling(models.Model): self.ensure_one() self._check_not_allow_tukar_guling_on_bu_pick() - # existing_tukar_guling = self.env['tukar.guling'].search([ - # ('operations', '=', self.operations.id), - # ('id', '!=', self.id), - # ('state', '!=', 'cancel'), - # ], limit=1) - # - # if existing_tukar_guling: - # raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) + existing_tukar_guling = self.env['tukar.guling'].search([ + ('operations', '=', self.operations.id), + ('id', '!=', self.id), + ('state', '!=', 'cancel'), + ], limit=1) + + if existing_tukar_guling: + raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations if picking.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") -- cgit v1.2.3 From 00f94ec61fad94ed49f646396292228187b50173 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 21 Oct 2025 11:46:47 +0700 Subject: add asset type in assets --- indoteknik_custom/models/account_asset.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_asset.py b/indoteknik_custom/models/account_asset.py index bd5f9adb..211ab229 100644 --- a/indoteknik_custom/models/account_asset.py +++ b/indoteknik_custom/models/account_asset.py @@ -4,6 +4,10 @@ from odoo.exceptions import AccessError, UserError, ValidationError class AccountAsset(models.Model): _inherit = 'account.asset.asset' + asset_type = fields.Selection(string='Tipe Aset', selection=[ + ('aset_gudang', ' Aset Gudang'), + ('aset_kantor', 'Aset Kantor'), + ], tracking=True ) def action_close_asset(self): for asset in self: -- cgit v1.2.3 From 58a6ca75b9b1bb07ea958ab4bfb553140daeb8f8 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 21 Oct 2025 16:05:39 +0700 Subject: (andri) fix add pum + fix lock view --- indoteknik_custom/models/advance_payment_request.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index cc3e57d6..b333fcc4 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -647,7 +647,8 @@ class AdvancePaymentRequest(models.Model): pum_ids = self.search([ ('user_id', '=', self.user_id.id), - ('status', '!=', 'reject') + ('status', '!=', 'reject'), + ('type_request', '=', 'pum') ]) active_pum_count = 0 @@ -684,7 +685,8 @@ class AdvancePaymentRequest(models.Model): pum_ids = self.search([ ('user_id', '=', user.id), - ('status', '!=', 'reject') + ('status', '!=', 'reject'), + ('type_request', '=', 'pum') ]) active_pum_count = 0 @@ -760,7 +762,7 @@ class AdvancePaymentUsageLine(models.Model): attachment_filename_pdf = fields.Char(string='Filename PDF') account_id = fields.Many2one( - 'account.account', string='Jenis Biaya', required=True, + 'account.account', string='Jenis Biaya', domain="[('id', 'in', [484, 486, 488, 506, 507, 625, 471, 519, 527, 528, 529, 530, 565])]" # ID Jenis Biaya yang dibutuhkan ) -- cgit v1.2.3 From a1862d78377710020c40361401af79766ea9ca70 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 11:48:36 +0700 Subject: fix sj tele message sent multiple chat bubble with same message --- indoteknik_custom/models/sj_tele.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index 3ef4b877..d3d7dfce 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -18,6 +18,7 @@ class SjTele(models.Model): sale_name = fields.Char(string='Sale Name') create_date = fields.Datetime(string='Create Date') date_doc_kirim = fields.Datetime(string='Tanggal Kirim SJ') + is_sent = fields.Boolean(default=False) def woi(self): bot_mqdd = '8203414501:AAHy_XwiUAVrgRM2EJzW7sZx9npRLITZpb8' @@ -27,7 +28,9 @@ class SjTele(models.Model): # chat_id_testing = '-4920864331' # api_testing = f'https://api.telegram.org/bot{bot_testing}' - data = self.search([], order='create_date asc') + data = self.search([('is_sent', '=', False)], order='create_date asc') + # Old + # data = self.search([], order='create_date asc') if not data: text = "✅ tidak ada data (semua sudah tercatat)." @@ -83,6 +86,9 @@ class SjTele(models.Model): _logger.exception("Gagal kirim Telegram (batch %s-%s): %s", i + 1, min(i + BUB, len(lines)), e) time.sleep(5) # jeda kecil biar rapi & aman rate limit + # Set sent = true ketika sudah terkirim + data.write({'is_sent': True}) + return True # header = "Berikut merupakan nomor BU/OUT yang belum ada di Logbook SJ report:\n" -- cgit v1.2.3 From 9f916d880c5d061ee2ba66a80d26fbca76355054 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 11:49:37 +0700 Subject: fix sj tele message sent multiple chat bubble with same message --- indoteknik_custom/models/sj_tele.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index d3d7dfce..53ba26fc 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -28,7 +28,9 @@ class SjTele(models.Model): # chat_id_testing = '-4920864331' # api_testing = f'https://api.telegram.org/bot{bot_testing}' + # Select Data data = self.search([('is_sent', '=', False)], order='create_date asc') + # Old # data = self.search([], order='create_date asc') -- cgit v1.2.3 From d3e1c12c5745f95551116723efd64e7a15dd5fc1 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 22 Oct 2025 13:49:38 +0700 Subject: push --- indoteknik_custom/models/mrp_production.py | 3 +++ indoteknik_custom/models/stock_picking.py | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 30956082..02679458 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -308,6 +308,9 @@ class CheckBomProduct(models.Model): if not self.code_product: return + if self.production_id.qty_producing == 0: + raise UserError("Isi dan Save dahulu Quantity To Produce yang diinginkan!") + # Cari product berdasarkan default_code, barcode, atau barcode_box product = self.env['product.product'].search([ '|', diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 4772c433..d6096cc0 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -464,15 +464,15 @@ class StockPicking(models.Model): rec.last_update_date_doc_kirim = datetime.datetime.utcnow() - @api.constrains('scan_koli_lines') - def _constrains_scan_koli_lines(self): - now = datetime.datetime.utcnow() - for picking in self: - if len(picking.scan_koli_lines) > 0: - if len(picking.scan_koli_lines) != picking.total_mapping_koli: - raise UserError("Scan Koli Tidak Sesuai Dengan Total Mapping Koli") + # @api.constrains('scan_koli_lines') + # def _constrains_scan_koli_lines(self): + # now = datetime.datetime.utcnow() + # for picking in self: + # if len(picking.scan_koli_lines) > 0: + # if len(picking.scan_koli_lines) != picking.total_mapping_koli: + # raise UserError("Scan Koli Tidak Sesuai Dengan Total Mapping Koli") - picking.driver_departure_date = now + # picking.driver_departure_date = now @api.depends('total_so_koli') def _compute_total_so_koli(self): @@ -1303,6 +1303,9 @@ class StockPicking(models.Model): and self.create_date > threshold_datetime and not self.so_lama): raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) + + if 'BU/OUT/' in self.name: + self.driver_departure_date = datetime.datetime.utcnow() # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': # raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) -- cgit v1.2.3 From a94708b3f16d6d1bfe5db971904a0b1d31f74bcd Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 23 Oct 2025 14:57:04 +0700 Subject: (andri) del done not realized --- .../models/advance_payment_request.py | 319 ++++++++++++--------- 1 file changed, 182 insertions(+), 137 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index b333fcc4..4e0e481b 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -87,6 +87,7 @@ class AdvancePaymentRequest(models.Model): ('fat', 'FAT'), ('it', 'IT'), ('hr_ga', 'HR & GA'), + ('pimpinan', 'Pimpinan') ], string='Departement Type', tracking=3, required=True) attachment_file_image = fields.Binary(string='Attachment Image', attachment_filename='attachment_filename_image') @@ -228,100 +229,103 @@ class AdvancePaymentRequest(models.Model): 'filename': filename, } - def action_send_pum_reminder(self): - """ - Kirim email reminder PUM otomatis. - - Hari ini = kirim dengan template 'mail_template_pum_reminder_today' - - H-2 dari due date = kirim dengan template 'mail_template_pum_reminder_h_2' - """ - today = date.today() - pum_ids = self.search([ - ('date_back_to_office', '!=', False), - ('status', 'not in', ['draft', 'reject']), - ]) - - template_today = self.env.ref('indoteknik_custom.mail_template_pum_reminder_today', raise_if_not_found=False) - template_h2 = self.env.ref('indoteknik_custom.mail_template_pum_reminder_h_2', raise_if_not_found=False) - - if not template_today or not template_h2: - _logger.warning("Salah satu template email tidak ditemukan.") - return - - for pum in pum_ids: - _logger.info(f"[REMINDER] Memproses PUM {pum.number}") - - if not pum.email_ap or not pum.user_id.partner_id.email: - _logger.warning(f"[REMINDER] Lewati PUM {pum.number} karena email_ap atau email user kosong.") - continue - - due_date = pum.date_back_to_office + timedelta(days=7) - days_remaining = (due_date - today).days - - realization = self.env['advance.payment.settlement'].search([('pum_id', '=', pum.id)], limit=1) - if not realization or realization.done_status != 'remaining': - _logger.info(f"[REMINDER] Lewati PUM {pum.number}, status realisasi bukan 'remaining'.") - continue - - # Tentukan template - if pum.date_back_to_office == today: - template = template_today - elif days_remaining == 2: - template = template_h2 - else: - _logger.info(f"[REMINDER] Lewati PUM {pum.number}, hari ini bukan tanggal pengingat.") - continue - - # Generate attachment - try: - attachment_vals = pum._get_jasper_attachment() - attachment = self.env['ir.attachment'].create({ - 'name': attachment_vals['name'], - 'type': 'binary', - 'datas': attachment_vals['datas'], - 'res_model': 'advance.payment.request', - 'res_id': pum.id, - 'mimetype': 'application/pdf', - }) - except Exception as e: - _logger.error(f"[REMINDER] Gagal membuat attachment untuk PUM {pum.number}: {str(e)}") - continue - - email_values = { - # 'email_to': pum.user_id.partner_id.email, - 'email_to': 'andrifebriyadiputra@gmail.com', - 'email_from': pum.email_ap, - 'attachment_ids': [(6, 0, [attachment.id])], - } - - _logger.info(f"[REMINDER] Mengirim email PUM {pum.number} ke {email_values['email_to']} dari {email_values['email_from']}") - - try: - body_html = template._render_field('body_html', [pum.id])[pum.id] - - template.send_mail(pum.id, force_send=True, email_values=email_values) - _logger.info(f"[REMINDER] Email berhasil dikirim untuk PUM {pum.number}") - - # Post info sederhana - pum.message_post( - body="Email Reminder Berhasil dikirimkan", - message_type="comment", - subtype_xmlid="mail.mt_note", - ) - - user_system = self.env['res.users'].browse(25) - system_id = user_system.partner_id.id if user_system else False - - # Post isi email ke chatter - pum.message_post( - body=body_html, - message_type="comment", - subtype_xmlid="mail.mt_note", - author_id=system_id, - ) - except Exception as e: - _logger.error(f"[REMINDER] Gagal mengirim email untuk PUM {pum.number}: {str(e)}") - - return True + # def action_send_pum_reminder(self): + # """ + # Kirim email reminder PUM otomatis. + # - Hari ini = kirim dengan template 'mail_template_pum_reminder_today' + # - H-2 dari due date = kirim dengan template 'mail_template_pum_reminder_h_2' + # """ + # today = date.today() + # pum_ids = self.search([ + # ('date_back_to_office', '!=', False), + # ('status', 'not in', ['draft', 'reject']), + # ]) + + # template_today = self.env.ref('indoteknik_custom.mail_template_pum_reminder_today', raise_if_not_found=False) + # template_h2 = self.env.ref('indoteknik_custom.mail_template_pum_reminder_h_2', raise_if_not_found=False) + + # if not template_today or not template_h2: + # _logger.warning("Salah satu template email tidak ditemukan.") + # return + + # for pum in pum_ids: + # _logger.info(f"[REMINDER] Memproses PUM {pum.number}") + + # if not pum.email_ap or not pum.user_id.partner_id.email: + # _logger.warning(f"[REMINDER] Lewati PUM {pum.number} karena email_ap atau email user kosong.") + # continue + + # due_date = pum.date_back_to_office + timedelta(days=7) + # days_remaining = (due_date - today).days + + # # --- PENYESUAIAN LOGIKA --- + # # Jika realisasi sudah dibuat (apapun status realisasinya), lewati. + # realization = self.env['advance.payment.settlement'].search([('pum_id', '=', pum.id)], limit=1) + # if realization: + # _logger.info(f"[REMINDER] Lewati PUM {pum.number}, realisasi sudah dibuat.") + # continue + # # --- BATAS PENYESUAIAN --- + + # # Tentukan template + # if pum.date_back_to_office == today: + # template = template_today + # elif days_remaining == 2: + # template = template_h2 + # else: + # _logger.info(f"[REMINDER] Lewati PUM {pum.number}, hari ini bukan tanggal pengingat.") + # continue + + # # Generate attachment + # try: + # attachment_vals = pum._get_jasper_attachment() + # attachment = self.env['ir.attachment'].create({ + # 'name': attachment_vals['name'], + # 'type': 'binary', + # 'datas': attachment_vals['datas'], + # 'res_model': 'advance.payment.request', + # 'res_id': pum.id, + # 'mimetype': 'application/pdf', + # }) + # except Exception as e: + # _logger.error(f"[REMINDER] Gagal membuat attachment untuk PUM {pum.number}: {str(e)}") + # continue + + # email_values = { + # # 'email_to': pum.user_id.partner_id.email, + # 'email_to': 'andrifebriyadiputra@gmail.com', + # 'email_from': pum.email_ap, + # 'attachment_ids': [(6, 0, [attachment.id])], + # } + + # _logger.info(f"[REMINDER] Mengirim email PUM {pum.number} ke {email_values['email_to']} dari {email_values['email_from']}") + + # try: + # body_html = template._render_field('body_html', [pum.id])[pum.id] + + # template.send_mail(pum.id, force_send=True, email_values=email_values) + # _logger.info(f"[REMINDER] Email berhasil dikirim untuk PUM {pum.number}") + + # # Post info sederhana + # pum.message_post( + # body="Email Reminder Berhasil dikirimkan", + # message_type="comment", + # subtype_xmlid="mail.mt_note", + # ) + + # user_system = self.env['res.users'].browse(25) + # system_id = user_system.partner_id.id if user_system else False + + # # Post isi email ke chatter + # pum.message_post( + # body=body_html, + # message_type="comment", + # subtype_xmlid="mail.mt_note", + # author_id=system_id, + # ) + # except Exception as e: + # _logger.error(f"[REMINDER] Gagal mengirim email untuk PUM {pum.number}: {str(e)}") + + # return True @api.depends('move_id.state') @@ -521,6 +525,7 @@ class AdvancePaymentRequest(models.Model): 'fat': 'Finance & Accounting Manager', 'it': 'IT Manager', 'hr_ga': 'HR & GA Manager', + 'pimpinan': 'Pimpinan', } rec.position_department = department_titles.get(rec.departement_type, 'Departement Manager') @@ -547,7 +552,7 @@ class AdvancePaymentRequest(models.Model): rec.message_post( body=f"Approval AP oleh {self.env.user.name} " - f"pada {formatted_date}." + f"pada {formatted_date}." ) elif rec.status == 'pengajuan3': @@ -561,7 +566,7 @@ class AdvancePaymentRequest(models.Model): rec.message_post( body=f"Approval Pimpinan oleh {self.env.user.name} " - f"pada {formatted_date}." + f"pada {formatted_date}." ) else: @@ -654,7 +659,7 @@ class AdvancePaymentRequest(models.Model): active_pum_count = 0 for pum in pum_ids: realization = self.env['advance.payment.settlement'].search([('pum_id', '=', pum.id)], limit=1) - if not realization or realization.done_status != 'done_not_realized': + if not realization: active_pum_count += 1 if active_pum_count >= 2: @@ -692,7 +697,7 @@ class AdvancePaymentRequest(models.Model): active_pum_count = 0 for pum in pum_ids: realization = self.env['advance.payment.settlement'].search([('pum_id', '=', pum.id)], limit=1) - if not realization or realization.done_status != 'done_not_realized': + if not realization: active_pum_count += 1 if active_pum_count >= 2 and vals.get('type_request') == 'pum': @@ -706,25 +711,45 @@ class AdvancePaymentRequest(models.Model): initial_status = '' position = vals.get('position_type') - if position == 'staff': - initial_status = 'pengajuan1' - elif position == 'manager': + department = vals.get('departement_type') + if department == 'hr_ga' or position in ('manager', 'pimpinan'): initial_status = 'pengajuan2' + else: + initial_status = 'pengajuan1' + + vals['status'] = initial_status + + if initial_status == 'pengajuan2' and department != 'hr_ga': applicant_name = vals.get('applicant_name') vals['name_approval_departement'] = self.env['res.users'].browse(applicant_name).name or '' vals['date_approved_department'] = now department_type = vals.get('departement_type') department_titles = self._get_department_titles_mapping() vals['position_department'] = department_titles.get(department_type, 'Departement Manager') - elif position == 'pimpinan': - initial_status = 'pengajuan2' - applicant_name = vals.get('applicant_name') - vals['name_approval_pimpinan'] = self.env['res.users'].browse(applicant_name).name or '' + + if position == 'pimpinan' and department != 'hr_ga': + vals['name_approval_pimpinan'] = self.env['res.users'].browse(vals.get('applicant_name')).name or '' vals['position_pimpinan'] = 'Pimpinan' vals['date_approved_pimpinan'] = now + # if position == 'staff': + # initial_status = 'pengajuan1' + # elif position == 'manager': + # initial_status = 'pengajuan2' + # applicant_name = vals.get('applicant_name') + # vals['name_approval_departement'] = self.env['res.users'].browse(applicant_name).name or '' + # vals['date_approved_department'] = now + # department_type = vals.get('departement_type') + # department_titles = self._get_department_titles_mapping() + # vals['position_department'] = department_titles.get(department_type, 'Departement Manager') + # elif position == 'pimpinan': + # initial_status = 'pengajuan2' + # applicant_name = vals.get('applicant_name') + # vals['name_approval_pimpinan'] = self.env['res.users'].browse(applicant_name).name or '' + # vals['position_pimpinan'] = 'Pimpinan' + # vals['date_approved_pimpinan'] = now + + # vals['status'] = initial_status - vals['status'] = initial_status - if not vals.get('number') or vals['number'] == 'New Draft': if vals.get('type_request') == 'reimburse': vals['number'] = self.env['ir.sequence'].next_by_code('reimburse.request') or 'New Draft' @@ -947,13 +972,14 @@ class AdvancePaymentSettlement(models.Model): ('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) + # --- DIHAPUS --- + # 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) + # --- BATAS DIHAPUS --- currency_id = fields.Many2one( 'res.currency', string='Currency', @@ -1076,24 +1102,26 @@ class AdvancePaymentSettlement(models.Model): rec.remaining_value = rec.nominal_pum - rec.grand_total_use return - def action_validation(self): - self.ensure_one() + # --- DIHAPUS --- + # 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}.") + # # 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}.") + # --- BATAS DIHAPUS --- def action_cab(self): self.ensure_one() @@ -1119,8 +1147,10 @@ class AdvancePaymentSettlement(models.Model): # Account Uang Muka Operasional account_uang_muka = 403 + # --- PENYESUAIAN LOGIKA --- # Tanggal pakai create_date atau hari ini - account_date = self.date_done_not_realized or fields.Date.today() + account_date = fields.Date.context_today(self) + # --- BATAS PENYESUAIAN --- ref_label = f"Realisasi {self.pum_id.number} Biaya {self.pum_id.detail_note} ({cab_move.name})" @@ -1209,6 +1239,7 @@ class AdvancePaymentSettlement(models.Model): 'fat': 'Finance & Accounting Manager', 'it': 'IT Manager', 'hr_ga': 'HR & GA Manager', + 'pimpinan': 'Pimpinan', } rec.position_department = department_titles.get(rec.pum_id.departement_type, 'Departement Manager') @@ -1242,7 +1273,9 @@ class AdvancePaymentSettlement(models.Model): rec.date_approved_pimpinan = now rec.position_pimpinan = 'Pimpinan' rec.status = 'approved' - rec.done_status = 'done_not_realized' # Set status done untuk realisasi + # --- DIHAPUS --- + # rec.done_status = 'done_not_realized' # Set status done untuk realisasi + # --- BATAS DIHAPUS --- rec.message_post( body=f"Approval Pimpinan oleh {self.env.user.name} " @@ -1266,15 +1299,24 @@ class AdvancePaymentSettlement(models.Model): @api.model def create(self, vals): jakarta_tz = pytz.timezone('Asia/Jakarta') + # --- PENYESUAIAN LOGIKA WAKTU --- now = datetime.now(jakarta_tz).replace(tzinfo=None) + # Gunakan fields.Datetime.now() agar konsisten + # now = fields.Datetime.now() + # --- BATAS PENYESUAIAN --- + pum_id = vals.get('pum_id') initial_status = '' if pum_id: pum_request = self.env['advance.payment.request'].browse(pum_id) if pum_request: - position_dari_pum = pum_request.position_type + position_dari_pum = pum_request.position_type + department_dari_pum = pum_request.departement_type if position_dari_pum == 'staff': - initial_status = 'pengajuan1' + if department_dari_pum == 'hr_ga': + initial_status = 'pengajuan2' + else: + initial_status = 'pengajuan1' elif position_dari_pum == 'manager': initial_status = 'pengajuan2' applicant_name_str = pum_request.applicant_name.name or '' @@ -1289,9 +1331,12 @@ class AdvancePaymentSettlement(models.Model): applicant_name_str = pum_request.applicant_name.name or '' vals['date_approved_pimpinan'] = now vals['name_approval_pimpinan'] = applicant_name_str - vals['position_department'] = 'Pimpinan' + vals['position_pimpinan'] = 'Pimpinan' + + # --- PENYESUAIAN LOGIKA: SET DEFAULT JIKA KOSONG --- + vals['status'] = initial_status or 'pengajuan1' + # --- BATAS PENYESUAIAN --- - vals['status'] = initial_status rec = super().create(vals) rec._check_remaining_value() return rec -- cgit v1.2.3 From 51f06a7e343989221ca5ab8be6b94ebb1d060f82 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 23 Oct 2025 16:33:31 +0700 Subject: (andri) fix label --- indoteknik_custom/models/advance_payment_request.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 4e0e481b..722b01f2 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -174,6 +174,7 @@ class AdvancePaymentRequest(models.Model): 'target': 'new', 'context': { 'default_request_id': self.id, + 'default_total_reimburse': self.grand_total_reimburse, } } @@ -1152,7 +1153,7 @@ class AdvancePaymentSettlement(models.Model): account_date = fields.Date.context_today(self) # --- BATAS PENYESUAIAN --- - ref_label = f"Realisasi {self.pum_id.number} Biaya {self.pum_id.detail_note} ({cab_move.name})" + ref_label = f"Realisasi {self.pum_id.number} {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})" @@ -1367,7 +1368,7 @@ class AdvancePaymentCreateBill(models.TransientModel): apr = self.apr_id partner_id = apr.user_id.partner_id.id - ref_label = f'{apr.number} - Biaya {apr.detail_note or "-"}' + ref_label = f'{apr.number} - {apr.detail_note or "-"}' move = self.env['account.move'].create({ 'ref': ref_label, @@ -1422,7 +1423,6 @@ class CreateReimburseCabWizard(models.TransientModel): total_reimburse = fields.Monetary( string='Total Reimburse', related='request_id.grand_total_reimburse', - readonly=True ) currency_id = fields.Many2one(related='request_id.currency_id', readonly=True) @@ -1459,12 +1459,14 @@ class CreateReimburseCabWizard(models.TransientModel): 'partner_id': partner_id, 'name': f'Reimburse {request.number}', 'debit': 0, - 'credit': request.total_reimburse, + 'credit': request.grand_total_reimburse, })) + ref_label = f'{request.number} - {request.detail_note or "-"}' + # 3. Buat Journal Entry move = self.env['account.move'].create({ - 'ref': request.number, + 'ref': ref_label, 'date': fields.Date.context_today(self), 'journal_id': 11, # PENTING: Ganti 11 dengan ID Journal "Miscellaneous" Anda 'line_ids': lines, -- cgit v1.2.3 From 12f1df2b2dabd0fba5dc0e2584c9a588c378118c Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 23 Oct 2025 16:54:58 +0700 Subject: push maintenance partial delivery wizard --- indoteknik_custom/models/sale_order.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index a5e2f7c4..5c8f34c6 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -399,20 +399,21 @@ class SaleOrder(models.Model): ) def action_open_partial_delivery_wizard(self): - self.ensure_one() - pickings = self.picking_ids.filtered(lambda p: p.state not in ['done', 'cancel'] and p.name and 'BU/PICK/' in p.name) - return { - 'type': 'ir.actions.act_window', - 'name': 'Partial Delivery', - 'res_model': 'partial.delivery.wizard', - 'view_mode': 'form', - 'target': 'new', - 'context': { - 'default_sale_id': self.id, - # kasih langsung list of int biar ga ribet di wizard - 'default_picking_ids': pickings.ids, - } - } + raise UserError("Fitur ini sedang dalam pengembangan") + # self.ensure_one() + # pickings = self.picking_ids.filtered(lambda p: p.state not in ['done', 'cancel'] and p.name and 'BU/PICK/' in p.name) + # return { + # 'type': 'ir.actions.act_window', + # 'name': 'Partial Delivery', + # 'res_model': 'partial.delivery.wizard', + # 'view_mode': 'form', + # 'target': 'new', + # 'context': { + # 'default_sale_id': self.id, + # # kasih langsung list of int biar ga ribet di wizard + # 'default_picking_ids': pickings.ids, + # } + # } @api.depends('partner_id.is_cbd_locked') -- cgit v1.2.3 From e7264cf6995755f3a55e0d1eb9059354a56c552f Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 24 Oct 2025 10:11:01 +0700 Subject: (andri) fix --- indoteknik_custom/models/advance_payment_request.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 722b01f2..a2c39b52 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -463,9 +463,9 @@ class AdvancePaymentRequest(models.Model): jakarta_tz = pytz.timezone('Asia/Jakarta') now = datetime.now(jakarta_tz).replace(tzinfo=None) - 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.') + # 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.') for rec in self: if not rec.attachment_file_image and not rec.attachment_file_pdf: -- cgit v1.2.3 From 4b257da93f1eb1d025303113d6884b22df59c1b4 Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Fri, 24 Oct 2025 11:27:57 +0700 Subject: refund barang kurang --- indoteknik_custom/models/refund_sale_order.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index 47565dfc..e6547a88 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -753,10 +753,11 @@ class RefundSaleOrder(models.Model): line_vals = [] for so in self.sale_order_ids: for line in so.order_line: - if line.qty_delivered == 0: + barang_kurang = line.product_uom_qty - line.qty_delivered + if line.qty_delivered == 0 or barang_kurang > 0: line_vals.append((0, 0, { 'product_id': line.product_id.id, - 'quantity': line.product_uom_qty, + 'quantity': barang_kurang, 'from_name': so.name, 'prod_id': so.id, 'reason': '', -- cgit v1.2.3 From dc57520d3e0fe0bb8a314e1cb4ec4b3648fd8a37 Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Fri, 24 Oct 2025 11:29:00 +0700 Subject: push --- indoteknik_custom/models/refund_sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index e6547a88..475f0355 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -754,7 +754,7 @@ class RefundSaleOrder(models.Model): for so in self.sale_order_ids: for line in so.order_line: barang_kurang = line.product_uom_qty - line.qty_delivered - if line.qty_delivered == 0 or barang_kurang > 0: + if line.qty_delivered == 0 or barang_kurang >= 0: line_vals.append((0, 0, { 'product_id': line.product_id.id, 'quantity': barang_kurang, -- cgit v1.2.3 From 64fcfa3daf65c8f5699544ce73d7ec930fa124af Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Fri, 24 Oct 2025 11:35:13 +0700 Subject: push --- indoteknik_custom/models/refund_sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index 475f0355..e6547a88 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -754,7 +754,7 @@ class RefundSaleOrder(models.Model): for so in self.sale_order_ids: for line in so.order_line: barang_kurang = line.product_uom_qty - line.qty_delivered - if line.qty_delivered == 0 or barang_kurang >= 0: + if line.qty_delivered == 0 or barang_kurang > 0: line_vals.append((0, 0, { 'product_id': line.product_id.id, 'quantity': barang_kurang, -- cgit v1.2.3 From 2ee70365c70cb8de9570f456301dc85e5fc21ff7 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 25 Oct 2025 09:59:50 +0700 Subject: solr log --- indoteknik_custom/models/solr/apache_solr_queue.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/solr/apache_solr_queue.py b/indoteknik_custom/models/solr/apache_solr_queue.py index 1b51538f..ab03d569 100644 --- a/indoteknik_custom/models/solr/apache_solr_queue.py +++ b/indoteknik_custom/models/solr/apache_solr_queue.py @@ -1,10 +1,10 @@ from odoo import models, fields from datetime import datetime, timedelta -import logging, time - +import logging, time, traceback # <-- tambah traceback _logger = logging.getLogger(__name__) + class ApacheSolrQueue(models.Model): _name = 'apache.solr.queue' @@ -19,6 +19,7 @@ class ApacheSolrQueue(models.Model): ], 'Execute Status') execute_date = fields.Datetime('Execute Date') description = fields.Text('Description') + log = fields.Text('Log') def _compute_display_name(self): for rec in self: @@ -39,7 +40,7 @@ class ApacheSolrQueue(models.Model): if elapsed_time > max_exec_time: break rec.execute_queue() - + def open_target_record(self): return { 'name': '', @@ -70,14 +71,17 @@ class ApacheSolrQueue(models.Model): else: rec.execute_status = 'not_found' except Exception as e: - rec.description = e + # simpan error ringkas + traceback lengkap + rec.description = str(e) + rec.log = traceback.format_exc() rec.execute_status = 'failed' + rec.execute_date = datetime.utcnow() self.env.cr.commit() def create_unique(self, payload={}): count = self.search_count([ - ('res_model', '=', payload['res_model']), + ('res_model', '=', payload['res_model']), ('res_id', '=', payload['res_id']), ('function_name', '=', payload['function_name']), ('execute_status', '=', False) @@ -90,8 +94,6 @@ class ApacheSolrQueue(models.Model): ('execute_status', '=', 'success'), ('execute_date', '>=', (datetime.utcnow() - timedelta(days=days_after))), ], limit=limit) - + for rec in solr: rec.unlink() - - -- cgit v1.2.3 From b91ff1da85f4d57770805a7f56ccb28a4d409c4d Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 25 Oct 2025 10:00:47 +0700 Subject: solr log --- indoteknik_custom/models/solr/apache_solr_queue.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/solr/apache_solr_queue.py b/indoteknik_custom/models/solr/apache_solr_queue.py index ab03d569..3d6bd733 100644 --- a/indoteknik_custom/models/solr/apache_solr_queue.py +++ b/indoteknik_custom/models/solr/apache_solr_queue.py @@ -68,6 +68,7 @@ class ApacheSolrQueue(models.Model): if model_instance: getattr(model_instance, function_name)() rec.execute_status = 'success' + rec.log = traceback.format_exc() else: rec.execute_status = 'not_found' except Exception as e: -- cgit v1.2.3 From bbacaaa7fc8526a2831f9cdd890e25fb68f410ff Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 25 Oct 2025 12:01:36 +0700 Subject: solr image carousel and stock --- indoteknik_custom/models/solr/product_product.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/solr/product_product.py b/indoteknik_custom/models/solr/product_product.py index d8bc3973..7260c3ca 100644 --- a/indoteknik_custom/models/solr/product_product.py +++ b/indoteknik_custom/models/solr/product_product.py @@ -69,9 +69,9 @@ class ProductProduct(models.Model): 'product_id_i': variant.id, 'template_id_i': variant.product_tmpl_id.id, 'image_s': ir_attachment.api_image('product.template', 'image_512', variant.product_tmpl_id.id), - 'image_carousel_s': [ir_attachment.api_image('image.carousel', 'image', carousel.id) for carousel in variant.product_tmpl_id.image_carousel_lines], + 'image_carousel_ss': [ir_attachment.api_image('image.carousel', 'image', carousel.id) for carousel in variant.product_tmpl_id.image_carousel_lines], 'image_mobile_s': ir_attachment.api_image('product.template', 'image_256', variant.product_tmpl_id.id), - 'stock_total_f': variant.qty_stock_vendor, + 'stock_total_f': variant.qty_free_bandengan, 'weight_f': variant.weight, 'manufacture_id_i': variant.product_tmpl_id.x_manufacture.id or 0, 'manufacture_name_s': variant.product_tmpl_id.x_manufacture.x_name or '', -- cgit v1.2.3 From dbb3e64d43a5e5752a31879bc7be88d54c67d11c Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 27 Oct 2025 08:53:02 +0700 Subject: push fix bug partial delivery --- indoteknik_custom/models/account_payment.py | 2 +- indoteknik_custom/models/partial_delivery.py | 10 ++++++++++ indoteknik_custom/models/sale_order.py | 30 ++++++++++++++-------------- 3 files changed, 26 insertions(+), 16 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_payment.py b/indoteknik_custom/models/account_payment.py index 11c664eb..d2d3d175 100644 --- a/indoteknik_custom/models/account_payment.py +++ b/indoteknik_custom/models/account_payment.py @@ -42,7 +42,7 @@ class AccountPayment(models.Model): def allocate_invoices(self): for payment in self: - if self. + # if self. for line in payment.payment_line: invoice = line.account_move_id move_lines = payment.line_ids.filtered(lambda line: line.account_internal_type in ('receivable', 'payable')) diff --git a/indoteknik_custom/models/partial_delivery.py b/indoteknik_custom/models/partial_delivery.py index 4df7da1e..744db325 100644 --- a/indoteknik_custom/models/partial_delivery.py +++ b/indoteknik_custom/models/partial_delivery.py @@ -172,9 +172,11 @@ class PartialDeliveryWizard(models.TransientModel): for line in selected_lines: if line.selected_qty > line.reserved_qty: raise UserError(_("Jumlah produk %s yang dipilih melebihi jumlah reserved.") % line.product_id.display_name) + move = line.move_id move._do_unreserve() + # 🔹 Kalau cuma selected tanpa qty → anggap kirim semua reserved qty if line.selected and not line.selected_qty: line.selected_qty = line.reserved_qty @@ -186,12 +188,20 @@ class PartialDeliveryWizard(models.TransientModel): if line.selected_qty < move.product_uom_qty: qty_to_keep = move.product_uom_qty - line.selected_qty + new_move = move.copy(default={ 'product_uom_qty': line.selected_qty, 'picking_id': new_picking.id, 'partial': True, }) + + if move.move_dest_ids: + for dest_move in move.move_dest_ids: + # dest_move.write({'move_orig_ids': [(4, new_move.id)]}) + new_move.write({'move_dest_ids': [(4, dest_move.id)]}) + move.write({'product_uom_qty': qty_to_keep}) + else: move.write({'picking_id': new_picking.id, 'partial': True}) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 5c8f34c6..e254ef41 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -399,21 +399,21 @@ class SaleOrder(models.Model): ) def action_open_partial_delivery_wizard(self): - raise UserError("Fitur ini sedang dalam pengembangan") - # self.ensure_one() - # pickings = self.picking_ids.filtered(lambda p: p.state not in ['done', 'cancel'] and p.name and 'BU/PICK/' in p.name) - # return { - # 'type': 'ir.actions.act_window', - # 'name': 'Partial Delivery', - # 'res_model': 'partial.delivery.wizard', - # 'view_mode': 'form', - # 'target': 'new', - # 'context': { - # 'default_sale_id': self.id, - # # kasih langsung list of int biar ga ribet di wizard - # 'default_picking_ids': pickings.ids, - # } - # } + # raise UserError("Fitur ini sedang dalam pengembangan") + self.ensure_one() + pickings = self.picking_ids.filtered(lambda p: p.state not in ['done', 'cancel'] and p.name and 'BU/PICK/' in p.name) + return { + 'type': 'ir.actions.act_window', + 'name': 'Partial Delivery', + 'res_model': 'partial.delivery.wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_sale_id': self.id, + # kasih langsung list of int biar ga ribet di wizard + 'default_picking_ids': pickings.ids, + } + } @api.depends('partner_id.is_cbd_locked') -- cgit v1.2.3 From bc15f6c49970e6b57a3be07327575ca85a66bb5b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 27 Oct 2025 09:00:34 +0700 Subject: fix voucher solr error --- indoteknik_custom/models/solr/product_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/solr/product_template.py b/indoteknik_custom/models/solr/product_template.py index c4aefe19..a7beca12 100644 --- a/indoteknik_custom/models/solr/product_template.py +++ b/indoteknik_custom/models/solr/product_template.py @@ -59,9 +59,9 @@ class ProductTemplate(models.Model): solr_model = self.env['apache.solr'] for template in self: + voucher = None if template.x_manufacture: voucher = self.get_voucher_pastihemat(template.x_manufacture.id) - # Lakukan sesuatu dengan voucher variant_names = ', '.join([x.display_name or '' for x in template.product_variant_ids]) variant_codes = ', '.join([x.default_code or '' for x in template.product_variant_ids]) -- cgit v1.2.3 From 8952e591e6673b5e198f94a8e20598765a5b616a Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 27 Oct 2025 10:53:00 +0700 Subject: validation product category null --- indoteknik_custom/models/purchase_order.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index e79417aa..5a28c485 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1123,6 +1123,8 @@ class PurchaseOrder(models.Model): if not self.not_update_purchasepricelist: self.add_product_to_pricelist() for line in self.order_line: + if not line.product_id.public_categ_ids: + raise UserError("Product %s kategorinya kosong" % line.product_id.name) if not line.product_id.purchase_ok: raise UserError("Terdapat barang yang tidak bisa diproses") # Validasi pajak -- cgit v1.2.3 From a1eb9a300b03e2aa3a2925dc0c6340cfd97f5270 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 27 Oct 2025 13:09:56 +0700 Subject: push --- indoteknik_custom/models/purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 5a28c485..534d8122 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1043,7 +1043,7 @@ class PurchaseOrder(models.Model): # test = line.product_uom_qty # test2 = line.product_id.plafon_qty # test3 = test2 + line.product_uom_qty - if line.product_uom_qty > line.product_id.plafon_qty + line.product_uom_qty and not self.env.user.id == 21: + if line.product_uom_qty > line.product_id.plafon_qty + line.product_uom_qty and self.env.user.id not in [21, 7]: raise UserError('Product '+line.product_id.name+' melebihi plafon, harus Approval Rafly') def check_different_vendor_so_po(self): -- cgit v1.2.3 From 73b7658b46f62518274fec1fb1291d5a88a5ed5a Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 27 Oct 2025 15:41:09 +0700 Subject: (andri) final --- .../models/advance_payment_request.py | 121 +++++++++++++-------- 1 file changed, 76 insertions(+), 45 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index a2c39b52..06fb7917 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -15,6 +15,7 @@ class AdvancePaymentRequest(models.Model): _description = 'Advance Payment Request or Reimburse' _rec_name = 'number' _inherit = ['mail.thread', 'mail.activity.mixin'] + _order = 'create_date desc' 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) @@ -233,49 +234,78 @@ class AdvancePaymentRequest(models.Model): # def action_send_pum_reminder(self): # """ # Kirim email reminder PUM otomatis. - # - Hari ini = kirim dengan template 'mail_template_pum_reminder_today' - # - H-2 dari due date = kirim dengan template 'mail_template_pum_reminder_h_2' + # - PUM Perjalanan: + # - Hari H kembali ke kantor = template 'mail_template_pum_reminder_today' + # - H-2 dari due date = template 'mail_template_pum_reminder_h_2' + # - PUM Non-Perjalanan: + # - H-2 dari due date = template 'mail_template_pum_reminder_h_2' # """ # today = date.today() + + # # Penyesuaian 1: Cari semua PUM yang sudah disetujui (bukan draft/reject) + # # Kita tidak filter 'date_back_to_office' di sini lagi. # pum_ids = self.search([ - # ('date_back_to_office', '!=', False), # ('status', 'not in', ['draft', 'reject']), + # ('type_request', '=', 'pum') # ]) # template_today = self.env.ref('indoteknik_custom.mail_template_pum_reminder_today', raise_if_not_found=False) # template_h2 = self.env.ref('indoteknik_custom.mail_template_pum_reminder_h_2', raise_if_not_found=False) # if not template_today or not template_h2: - # _logger.warning("Salah satu template email tidak ditemukan.") + # _logger.warning("Salah satu template email PUM (today/h2) tidak ditemukan.") # return # for pum in pum_ids: # _logger.info(f"[REMINDER] Memproses PUM {pum.number}") + # # Penyesuaian 2: Logika ini sudah benar (sesuai update kita sebelumnya) + # # Jika realisasi sudah dibuat, PUM tidak aktif lagi, lewati. + # realization = self.env['advance.payment.settlement'].search([('pum_id', '=', pum.id)], limit=1) + # if realization: + # _logger.info(f"[REMINDER] Lewati PUM {pum.number}, realisasi sudah dibuat.") + # continue + # if not pum.email_ap or not pum.user_id.partner_id.email: # _logger.warning(f"[REMINDER] Lewati PUM {pum.number} karena email_ap atau email user kosong.") # continue - # due_date = pum.date_back_to_office + timedelta(days=7) + # # Penyesuaian 3: Logika penentuan Due Date (Wajib) + # due_date = False + # base_date_for_today_check = False # Khusus PUM Perjalanan + + # if pum.apr_perjalanan: + # if pum.date_back_to_office: + # due_date = pum.date_back_to_office + timedelta(days=7) + # base_date_for_today_check = pum.date_back_to_office + # else: + # _logger.warning(f"[REMINDER] Lewati PUM {pum.number} (perjalanan) karena tgl kembali kosong.") + # continue + # else: + # # Ini adalah PUM Non-Perjalanan + # if not pum.create_date: + # _logger.warning(f"[REMINDER] Lewati PUM {pum.number} (non-perjalanan) karena create_date kosong.") + # continue + # base_date = pum.create_date.date() + # due_date = base_date + timedelta(days=7) + + # # Hitung sisa hari # days_remaining = (due_date - today).days - # # --- PENYESUAIAN LOGIKA --- - # # Jika realisasi sudah dibuat (apapun status realisasinya), lewati. - # realization = self.env['advance.payment.settlement'].search([('pum_id', '=', pum.id)], limit=1) - # if realization: - # _logger.info(f"[REMINDER] Lewati PUM {pum.number}, realisasi sudah dibuat.") - # continue - # # --- BATAS PENYESUAIAN --- - - # # Tentukan template - # if pum.date_back_to_office == today: + # # Penyesuaian 4: Tentukan template berdasarkan sisa hari + # template = False + # if pum.apr_perjalanan and base_date_for_today_check == today: + # # Hari H kembali ke kantor (HANYA PUM Perjalanan) # template = template_today # elif days_remaining == 2: + # # H-2 due date (Untuk SEMUA jenis PUM) # template = template_h2 # else: - # _logger.info(f"[REMINDER] Lewati PUM {pum.number}, hari ini bukan tanggal pengingat.") + # _logger.info(f"[REMINDER] Lewati PUM {pum.number}, hari ini bukan tgl pengingat (Sisa hari: {days_remaining}).") # continue + # # --- Sisanya (Generate attachment & kirim email) sudah aman --- + # # Generate attachment # try: # attachment_vals = pum._get_jasper_attachment() @@ -293,16 +323,16 @@ class AdvancePaymentRequest(models.Model): # email_values = { # # 'email_to': pum.user_id.partner_id.email, - # 'email_to': 'andrifebriyadiputra@gmail.com', + # 'email_to': 'andrifebriyadiputra@gmail.com', # Masih hardcode # 'email_from': pum.email_ap, - # 'attachment_ids': [(6, 0, [attachment.id])], + # 'email_from': 'finance@indoteknik.co.id', + # 'attachment_ids': [(6, 0, [attachment.id])] # } # _logger.info(f"[REMINDER] Mengirim email PUM {pum.number} ke {email_values['email_to']} dari {email_values['email_from']}") # try: # body_html = template._render_field('body_html', [pum.id])[pum.id] - # template.send_mail(pum.id, force_send=True, email_values=email_values) # _logger.info(f"[REMINDER] Email berhasil dikirim untuk PUM {pum.number}") @@ -338,9 +368,9 @@ class AdvancePaymentRequest(models.Model): 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.') + 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.") @@ -460,12 +490,12 @@ class AdvancePaymentRequest(models.Model): def action_confirm_payment(self): - jakarta_tz = pytz.timezone('Asia/Jakarta') - now = datetime.now(jakarta_tz).replace(tzinfo=None) + # jakarta_tz = pytz.timezone('Asia/Jakarta') + # now = datetime.now(jakarta_tz).replace(tzinfo=None) - # 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.') + 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.') for rec in self: if not rec.attachment_file_image and not rec.attachment_file_pdf: @@ -475,10 +505,10 @@ class AdvancePaymentRequest(models.Model): ) rec.status_pay_down_payment = 'payment' - rec.upload_attachment_date = now + rec.upload_attachment_date = datetime.utcnow() rec.message_post( - body="Status pembayaran telah dikonfirmasi oleh AP.", + body="Bukti transfer telah di upload oleh Finance AP.", message_type="comment", subtype_xmlid="mail.mt_note", ) @@ -538,9 +568,9 @@ class AdvancePaymentRequest(models.Model): ) 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.") + 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.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap rec.email_ap = self.env.user.email @@ -580,9 +610,9 @@ class AdvancePaymentRequest(models.Model): 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.') + 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.') @@ -604,7 +634,7 @@ class AdvancePaymentRequest(models.Model): def _compute_days_remaining(self): today = date.today() for rec in self: - if rec.status in ['approved', 'reject'] and rec.days_remaining: + if rec.status in ['approved'] and rec.days_remaining: continue if rec.apr_perjalanan: @@ -767,7 +797,7 @@ class AdvancePaymentUsageLine(models.Model): realization_id = fields.Many2one('advance.payment.settlement', string='Realization') date = fields.Date(string='Tanggal', required=True, default=fields.Date.today) - description = fields.Char(string='Description', required=True) + description = fields.Text(string='Description', required=True) nominal = fields.Float(string='Nominal', required=True) done_attachment = fields.Boolean(string='Checked', default=False) @@ -788,7 +818,7 @@ class AdvancePaymentUsageLine(models.Model): attachment_filename_pdf = fields.Char(string='Filename PDF') account_id = fields.Many2one( - 'account.account', string='Jenis Biaya', + 'account.account', string='Jenis Biaya', tracking=3, domain="[('id', 'in', [484, 486, 488, 506, 507, 625, 471, 519, 527, 528, 529, 530, 565])]" # ID Jenis Biaya yang dibutuhkan ) @@ -803,11 +833,11 @@ class AdvancePaymentUsageLine(models.Model): for line in self: line.is_current_user_ap = is_ap - @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('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): @@ -851,10 +881,10 @@ class ReimburseLine(models.Model): date = fields.Date(string='Tanggal', required=True, default=fields.Date.today) account_id = fields.Many2one( 'account.account', - string='Jenis Biaya', + string='Jenis Biaya', tracking=3, domain="[('id', 'in', [484, 486, 527, 529, 530, 471, 473, 492, 493, 488, 625, 528, 533, 534])]" ) - description = fields.Char(string='Description', required=True, tracking=3) + description = fields.Text(string='Description', required=True, tracking=3) distance_departure = fields.Float(string='Pergi (Km)', tracking=3) distance_return = fields.Float(string='Pulang (Km)', tracking=3) quantity = fields.Float(string='Quantity', tracking=3) @@ -926,6 +956,7 @@ class AdvancePaymentSettlement(models.Model): _description = 'Advance Payment Settlement' _inherit = ['mail.thread'] _rec_name = 'name' + _order = 'create_date desc' pum_id = fields.Many2one('advance.payment.request', string='No PUM', ondelete='cascade') name = fields.Char(string='Nama', readonly=True, tracking=3) -- cgit v1.2.3 From 87a4fc9ed43ea85cbbc8d09d268551c4f033491b Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 27 Oct 2025 16:05:38 +0700 Subject: push --- indoteknik_custom/models/tukar_guling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index cb630a04..f5b0c745 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -165,7 +165,7 @@ class TukarGuling(models.Model): @api.onchange('operations') def _onchange_operations(self): """Auto-populate lines ketika operations dipilih""" - if self.operations.picking_type_id.id not in [29, 30]: + if self.operations.picking_type_id.id not in [29, 30] and self.env.user.id != 1102: raise UserError("❌ Picking type harus BU/OUT atau BU/PICK") for rec in self: if rec.operations and rec.operations.picking_type_id.id == 30: @@ -412,7 +412,7 @@ class TukarGuling(models.Model): def write(self, vals): self.ensure_one() - if self.operations.picking_type_id.id not in [29, 30]: + if self.operations.picking_type_id.id not in [29, 30] and self.env.user.id != 1102: raise UserError("❌ Picking type harus BU/OUT atau BU/PICK") # self._check_invoice_on_retur_so() operasi = self.operations.picking_type_id.id -- cgit v1.2.3 From c15d83be7aad2a92abdd797ca7dd403566b8b841 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 27 Oct 2025 16:49:15 +0700 Subject: (andri) fix perhitungan sisa hari realisasi --- indoteknik_custom/models/advance_payment_request.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 06fb7917..140e48bb 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -630,11 +630,13 @@ class AdvancePaymentRequest(models.Model): } - @api.depends('date_back_to_office', 'status', 'apr_perjalanan', 'create_date') + @api.depends('date_back_to_office', 'status', 'apr_perjalanan', 'create_date', 'settlement_ids.status') def _compute_days_remaining(self): today = date.today() for rec in self: - if rec.status in ['approved'] and rec.days_remaining: + # if rec.status in ['approved'] and rec.days_remaining: + is_settlement_approved = any(s.status == 'approved' for s in rec.settlement_ids) + if is_settlement_approved: continue if rec.apr_perjalanan: @@ -652,7 +654,7 @@ class AdvancePaymentRequest(models.Model): base_date = rec.create_date.date() if rec.create_date else today due_date = base_date + timedelta(days=7) rec.estimated_return_date = due_date - rec.days_remaining = (due_date - base_date).days + rec.days_remaining = (due_date - today).days @api.onchange('date_back_to_office') def _onchange_date_back_to_office(self): -- cgit v1.2.3 From 2600c00e6944fd3d594fc0bd3a5a7d1e581d7b91 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 28 Oct 2025 11:08:55 +0700 Subject: (andri) show internal notes by contact di view SO dan Inv --- indoteknik_custom/models/account_move.py | 1 + indoteknik_custom/models/sale_order.py | 1 + 2 files changed, 2 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 44b3cb76..684ef335 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -106,6 +106,7 @@ class AccountMove(models.Model): help="Tanggal janji bayar dari customer setelah reminder dikirim.", tracking=True ) + internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True, help="Internal Notes dari contact utama customer.") # def _check_and_lock_cbd(self): # cbd_term = self.env['account.payment.term'].browse(26) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e254ef41..e1fcb88a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -397,6 +397,7 @@ class SaleOrder(models.Model): string="Partner Locked CBD", compute="_compute_partner_is_cbd_locked" ) + internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True) def action_open_partial_delivery_wizard(self): # raise UserError("Fitur ini sedang dalam pengembangan") -- cgit v1.2.3 From cf4e1d832aff5dc31dd2fcdcc68afb2e22e97e09 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 28 Oct 2025 11:09:34 +0700 Subject: (andri) info --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e1fcb88a..d24725a7 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -397,7 +397,7 @@ class SaleOrder(models.Model): string="Partner Locked CBD", compute="_compute_partner_is_cbd_locked" ) - internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True) + internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True, help="Internal Notes dari contact utama customer.") def action_open_partial_delivery_wizard(self): # raise UserError("Fitur ini sedang dalam pengembangan") -- cgit v1.2.3 From be6ad2b51e04a1ce6e8160804d0556ab4ae807ab Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 28 Oct 2025 11:34:58 +0700 Subject: remove validation pernah di retur ccm --- indoteknik_custom/models/tukar_guling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index f5b0c745..99a74505 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -498,8 +498,8 @@ class TukarGuling(models.Model): ('state', '!=', 'cancel'), ], limit=1) - if existing_tukar_guling: - raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) + # if existing_tukar_guling: + # raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations if picking.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") -- cgit v1.2.3 From 5d9d801c1aba7f047a16ae6e0ac84655480fadae Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 28 Oct 2025 15:03:51 +0700 Subject: midtrans --- indoteknik_custom/models/sale_order.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d24725a7..6c144623 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1986,10 +1986,10 @@ class SaleOrder(models.Model): # raise UserError('Kelurahan Real Delivery Address harus diisi') def generate_payment_link_midtrans_sales_order(self): - midtrans_url = 'https://app.sandbox.midtrans.com/snap/v1/transactions' # dev - sandbox - midtrans_auth = 'Basic U0ItTWlkLXNlcnZlci1uLVY3ZDJjMlpCMFNWRUQyOU95Q1dWWXA6' # dev - sandbox - # midtrans_url = 'https://app.midtrans.com/snap/v1/transactions' # production - # midtrans_auth = 'Basic TWlkLXNlcnZlci1SbGMxZ2gzWGpSVW5scl9JblZzTV9OTnU6' # production + # midtrans_url = 'https://app.sandbox.midtrans.com/snap/v1/transactions' # dev - sandbox + # midtrans_auth = 'Basic U0ItTWlkLXNlcnZlci1uLVY3ZDJjMlpCMFNWRUQyOU95Q1dWWXA6' # dev - sandbox + midtrans_url = 'https://app.midtrans.com/snap/v1/transactions' # production + midtrans_auth = 'Basic TWlkLXNlcnZlci1SbGMxZ2gzWGpSVW5scl9JblZzTV9OTnU6' # production so_number = self.name so_number = so_number.replace('/', '-') @@ -2002,8 +2002,8 @@ class SaleOrder(models.Model): } # ==== ENV ==== - check_url = f'https://api.sandbox.midtrans.com/v2/{so_number}/status' # dev - sandbox - # check_url = f'https://api.midtrans.com/v2/{so_number}/status' # production + # check_url = f'https://api.sandbox.midtrans.com/v2/{so_number}/status' # dev - sandbox + check_url = f'https://api.midtrans.com/v2/{so_number}/status' # production # ============================================= check_response = requests.get(check_url, headers=headers) -- cgit v1.2.3 From b95b268f66a82c0d1703f9d7644d3575b63f345d Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 28 Oct 2025 17:32:46 +0700 Subject: (andri) fix days remaining compute + add autoset nominal reimburse based grandtotal --- .../models/advance_payment_request.py | 66 ++++++++++++++-------- 1 file changed, 43 insertions(+), 23 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 140e48bb..7be9b00d 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -39,8 +39,7 @@ class AdvancePaymentRequest(models.Model): estimated_return_date = fields.Date( string='Batas Pengajuan', - help='Tanggal batas maksimal pengajuan realisasi setelah kembali ke kantor. ' - '7 hari setelah tanggal kembali.' + help='Tanggal batas maksimal durasi pengajuan realisasi' ) days_remaining = fields.Integer( @@ -154,6 +153,11 @@ class AdvancePaymentRequest(models.Model): compute='_compute_is_current_user_ap' ) + @api.onchange('grand_total_reimburse', 'type_request') + def _onchange_reimburse_line_update_nominal(self): + if self.type_request == 'reimburse': + self.nominal = self.grand_total_reimburse + def _compute_is_current_user_ap(self): ap_user_ids = [23, 9468] is_ap = self.env.user.id in ap_user_ids @@ -630,31 +634,47 @@ class AdvancePaymentRequest(models.Model): } - @api.depends('date_back_to_office', 'status', 'apr_perjalanan', 'create_date', 'settlement_ids.status') + @api.depends('date_back_to_office', 'status', 'apr_perjalanan', 'create_date', 'settlement_ids.status', 'type_request') def _compute_days_remaining(self): today = date.today() for rec in self: - # if rec.status in ['approved'] and rec.days_remaining: - is_settlement_approved = any(s.status == 'approved' for s in rec.settlement_ids) - if is_settlement_approved: - continue - if rec.apr_perjalanan: - if rec.date_back_to_office: - due_date = rec.date_back_to_office + timedelta(days=7) - rec.estimated_return_date = due_date + current_days = rec.days_remaining or 0 + current_due_date = rec.estimated_return_date or False + if rec.type_request == 'pum': + is_settlement_approved = any(s.status == 'approved' for s in rec.settlement_ids) + if not is_settlement_approved: + due_date = False + + if rec.apr_perjalanan: + # Alur PUM Perjalanan + if rec.date_back_to_office: + due_date = rec.date_back_to_office + timedelta(days=7) + effective_today = max(today, rec.date_back_to_office) + + current_due_date = due_date + current_days = (due_date - effective_today).days + else: + + current_due_date = False + current_days = 0 + else: + # Alur PUM Non-Perjalanan + if rec.create_date: + base_date = rec.create_date.date() + due_date = base_date + timedelta(days=7) + + current_due_date = due_date + current_days = (due_date - today).days + else: + current_due_date = False + current_days = 0 + else: + current_due_date = False + current_days = 0 - # 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 - else: - base_date = rec.create_date.date() if rec.create_date else today - due_date = base_date + timedelta(days=7) - rec.estimated_return_date = due_date - rec.days_remaining = (due_date - today).days + rec.days_remaining = current_days + rec.estimated_return_date = current_due_date @api.onchange('date_back_to_office') def _onchange_date_back_to_office(self): @@ -889,7 +909,7 @@ class ReimburseLine(models.Model): description = fields.Text(string='Description', required=True, tracking=3) distance_departure = fields.Float(string='Pergi (Km)', tracking=3) distance_return = fields.Float(string='Pulang (Km)', tracking=3) - quantity = fields.Float(string='Quantity', tracking=3) + quantity = fields.Float(string='Quantity', tracking=3, default=1) price_unit = fields.Float(string='Price', tracking=3) total = fields.Float(string='Total', tracking=3, compute='_compute_total') # total = fields.Float(string='Total', tracking=3) -- cgit v1.2.3 From fe27356afbbc399fefea84692c1f04b5d05547e1 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 28 Oct 2025 17:42:59 +0700 Subject: (andri) fix save --- indoteknik_custom/models/advance_payment_request.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 7be9b00d..5a465ca4 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -810,7 +810,12 @@ class AdvancePaymentRequest(models.Model): vals['number'] = self.env['ir.sequence'].next_by_code('advance.payment.request') or 'New Draft' # vals['status'] = 'pengajuan1' - return super(AdvancePaymentRequest, self).create(vals) + # return super(AdvancePaymentRequest, self).create(vals) + rec = super(AdvancePaymentRequest, self).create(vals) + if rec.type_request == 'reimburse': + rec._compute_grand_total_reimburse() + rec.nominal = rec.grand_total_reimburse + return rec class AdvancePaymentUsageLine(models.Model): -- cgit v1.2.3 From 0c7c965fe378ecdf878911088bed19d72947a36f Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 29 Oct 2025 10:07:11 +0700 Subject: (andri) fix constraint cbdlock --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 6c144623..c99067df 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -423,7 +423,7 @@ class SaleOrder(models.Model): order.partner_is_cbd_locked = order.partner_id.is_cbd_locked - @api.constrains('payment_term_id', 'partner_id', 'state') + @api.constrains('payment_term_id', 'partner_id') def _check_cbd_lock_sale_order(self): cbd_term = self.env['account.payment.term'].browse(26) for rec in self: -- cgit v1.2.3 From b35a287e6c431375458d47407f28ccb3978d0194 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 29 Oct 2025 10:29:20 +0700 Subject: push cr approval so sales admin --- indoteknik_custom/models/sale_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index c99067df..7b60863e 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2717,10 +2717,10 @@ class SaleOrder(models.Model): return True if user.id in (3401, 20, 3988, 17340): # admin (fida, nabila, ninda) - return True + raise UserError("Yahaha gabisa confirm so, minta ke sales nya ajah") if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988): - return True + raise UserError("Yahaha gabisa confirm so, minta ke sales nya ajah") salesperson_id = self.user_id.id approver_id = user.id -- cgit v1.2.3 From 099bec753a310ec83ea3562a78c304dffb6d50d8 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 30 Oct 2025 10:09:00 +0700 Subject: mark partial delivery and delivery status --- indoteknik_custom/models/partial_delivery.py | 4 ++ indoteknik_custom/models/sale_order.py | 2 +- indoteknik_custom/models/stock_move.py | 50 +++++++++++++++ indoteknik_custom/models/stock_picking.py | 91 ++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/partial_delivery.py b/indoteknik_custom/models/partial_delivery.py index 744db325..519f505c 100644 --- a/indoteknik_custom/models/partial_delivery.py +++ b/indoteknik_custom/models/partial_delivery.py @@ -115,9 +115,13 @@ class PartialDeliveryWizard(models.TransientModel): raise UserError(_("Picking harus dalam status Ready (assigned).")) lines_by_qty = self.line_ids.filtered(lambda l: l.selected_qty > 0) + lines_validation = self.line_ids.filtered(lambda l: l.selected_qty > l.reserved_qty) lines_by_selected = self.line_ids.filtered(lambda l: l.selected and not l.selected_qty) selected_lines = lines_by_qty | lines_by_selected # gabung dua domain hasil filter + if lines_validation: + raise UserError(_("Jumlah yang dipilih melebihi jumlah yang terdapat di DO.")) + if not selected_lines: raise UserError(_("Tidak ada produk yang dipilih atau diisi jumlahnya.")) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 7b60863e..494aeaa2 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2720,7 +2720,7 @@ class SaleOrder(models.Model): raise UserError("Yahaha gabisa confirm so, minta ke sales nya ajah") if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988): - raise UserError("Yahaha gabisa confirm so, minta ke sales nya ajah") + return True salesperson_id = self.user_id.id approver_id = user.id diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 1da2befe..8f8ba66f 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -186,6 +186,56 @@ class StockMoveLine(models.Model): line_no = fields.Integer('No', default=0) note = fields.Char('Note') manufacture = fields.Many2one('x_manufactures', string="Brands", related="product_id.x_manufacture", store=True) + outstanding_qty = fields.Float( + string='Outstanding Qty', + compute='_compute_delivery_line_status', + store=False + ) + delivery_status = fields.Selection([ + ('none', 'No Movement'), + ('partial', 'Partial'), + ('partial_final', 'Partial Final'), + ('full', 'Full'), + ], string='Delivery Status', compute='_compute_delivery_line_status', store=False) + + @api.depends('qty_done', 'product_uom_qty', 'picking_id.state') + def _compute_delivery_line_status(self): + for line in self: + line.outstanding_qty = 0.0 + line.delivery_status = 'none' + + picking = line.picking_id + if not picking or picking.picking_type_id.code != 'outgoing': + continue + + total_qty = line.move_id.product_uom_qty or 0 + done_qty = line.qty_done or 0 + + line.outstanding_qty = max(total_qty - done_qty, 0) + + if total_qty == 0: + continue + + if done_qty == 0: + line.delivery_status = 'none' + elif done_qty > 0: + has_other_out = self.env['stock.picking'].search_count([ + ('group_id', '=', picking.group_id.id), + ('name', 'ilike', 'BU/OUT'), + ('id', '!=', picking.id), + ('state', '=', 'done'), + ]) + if has_other_out and done_qty == total_qty: + line.delivery_status = 'partial_final' + elif not has_other_out and done_qty >= total_qty: + line.delivery_status = 'full' + elif has_other_out and done_qty < total_qty: + line.delivery_status = 'partial' + elif done_qty < total_qty: + line.delivery_status = 'partial' + else: + line.delivery_status = 'none' + # Ambil uom dari stock move @api.model diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index d6096cc0..7f8523a3 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -177,6 +177,97 @@ class StockPicking(models.Model): area_name = fields.Char(string="Area", compute="_compute_area_name") is_bu_iu = fields.Boolean('Is BU/IU', compute='_compute_is_bu_iu', default=False, copy=False, readonl=True) + qty_yang_mau_dikirim = fields.Float( + string='Qty yang Mau Dikirim', + compute='_compute_delivery_status_detail', + store=False + ) + qty_terkirim = fields.Float( + string='Qty Terkirim', + compute='_compute_delivery_status_detail', + store=False + ) + qty_gantung = fields.Float( + string='Qty Gantung', + compute='_compute_delivery_status_detail', + store=False + ) + delivery_status = fields.Selection([ + ('none', 'No Movement'), + ('partial', 'Partial'), + ('partial_final', 'Partial Final'), + ('full', 'Full'), + ], string='Delivery Status', compute='_compute_delivery_status_detail', store=False) + + @api.depends('move_line_ids_without_package.qty_done', 'move_line_ids_without_package.product_uom_qty', 'state') + def _compute_delivery_status_detail(self): + for picking in self: + # Default values + picking.qty_yang_mau_dikirim = 0.0 + picking.qty_terkirim = 0.0 + picking.qty_gantung = 0.0 + picking.delivery_status = 'none' + + # Hanya berlaku untuk pengiriman (BU/OUT) + if picking.picking_type_id.code != 'outgoing': + continue + + move_lines = picking.move_line_ids_without_package + if not move_lines: + continue + + # ====================== + # HITUNG QTY + # ====================== + total_qty = sum(line.product_uom_qty for line in move_lines) + + done_qty_total = sum(line.sale_line_id.qty_delivered for line in picking.move_ids_without_package) + order_qty_total = sum(line.sale_line_id.product_uom_qty for line in picking.move_ids_without_package) + gantung_qty_total = order_qty_total - done_qty_total - total_qty + + picking.qty_yang_mau_dikirim = total_qty + picking.qty_terkirim = done_qty_total + picking.qty_gantung = gantung_qty_total + + # if total_qty == 0: + # picking.delivery_status = 'none' + # continue + + # if done_qty_total == 0: + # picking.delivery_status = 'none' + # continue + + # ====================== + # CEK BU/OUT LAIN (BACKORDER) + # ====================== + has_other_out = self.env['stock.picking'].search_count([ + ('group_id', '=', picking.group_id.id), + ('name', 'ilike', 'BU/OUT'), + ('id', '!=', picking.id), + ('state', 'in', ['assigned', 'waiting', 'confirmed', 'done']), + ]) + + # ====================== + # LOGIKA STATUS + # ====================== + if gantung_qty_total == 0 and done_qty_total == 0: + # Semua barang udah terkirim, ga ada picking lain + picking.delivery_status = 'full' + + elif gantung_qty_total > 0 and total_qty > 0 and done_qty_total == 0: + # Masih ada picking lain dan sisa gantung → proses masih jalan + picking.delivery_status = 'partial' + + # elif gantung_qty_total > 0: + # # Ini picking terakhir, tapi qty belum full + # picking.delivery_status = 'partial_final' + + elif gantung_qty_total == 0 and done_qty_total > 0 and total_qty > 0: + # Udah kirim semua tapi masih ada picking lain (rare case) + picking.delivery_status = 'partial_final' + + else: + picking.delivery_status = 'none' @api.depends('name') def _compute_is_bu_iu(self): -- cgit v1.2.3 From 9c663e33bebc09d65614c5158c98270e964c9a06 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 30 Oct 2025 11:15:47 +0700 Subject: fix bug --- indoteknik_custom/models/sale_order.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 494aeaa2..b8de1697 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2716,11 +2716,13 @@ class SaleOrder(models.Model): if user.is_leader or user.is_sales_manager: return True - if user.id in (3401, 20, 3988, 17340): # admin (fida, nabila, ninda) + if not self.env.context.get("ask_approval") and user.id in (3401, 20, 3988, 17340): # admin (fida, nabila, ninda) raise UserError("Yahaha gabisa confirm so, minta ke sales nya ajah") - + if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988): return True + + salesperson_id = self.user_id.id approver_id = user.id -- cgit v1.2.3 From 21738b9d99acac49a041ab0c2a7dd99c94e3ed12 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 30 Oct 2025 15:43:06 +0700 Subject: fix gk bisa edit tanggal kirim sj --- indoteknik_custom/models/stock_move.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 8f8ba66f..43c4676e 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -219,12 +219,14 @@ class StockMoveLine(models.Model): if done_qty == 0: line.delivery_status = 'none' elif done_qty > 0: - has_other_out = self.env['stock.picking'].search_count([ - ('group_id', '=', picking.group_id.id), - ('name', 'ilike', 'BU/OUT'), - ('id', '!=', picking.id), - ('state', '=', 'done'), - ]) + has_other_out = 0 + if picking.group_id and isinstance(picking.id, int): + has_other_out = self.env['stock.picking'].search_count([ + ('group_id', '=', picking.group_id.id), + ('name', 'ilike', 'BU/OUT'), + ('id', '!=', picking.id), + ('state', '=', 'done'), + ]) if has_other_out and done_qty == total_qty: line.delivery_status = 'partial_final' elif not has_other_out and done_qty >= total_qty: @@ -236,7 +238,6 @@ class StockMoveLine(models.Model): else: line.delivery_status = 'none' - # Ambil uom dari stock move @api.model def create(self, vals): -- cgit v1.2.3 From 2a5062c7e292086df579420545f79801b80967e2 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 30 Oct 2025 15:56:55 +0700 Subject: (andri) fix partner jurnal --- indoteknik_custom/models/advance_payment_request.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 5a465ca4..66f0aac6 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -1194,7 +1194,8 @@ class AdvancePaymentSettlement(models.Model): 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 + # partner_id = self.pum_id.user_id.partner_id.id + partner_id = self.pum_id.applicant_name.partner_id.id cab_move = self.pum_id.move_id # Account Bank Intransit dari CAB: @@ -1424,7 +1425,8 @@ class AdvancePaymentCreateBill(models.TransientModel): # raise UserError('Hanya AP yang dapat menggunakan ini.') apr = self.apr_id - partner_id = apr.user_id.partner_id.id + # partner_id = apr.user_id.partner_id.id + partner_id = apr.applicant_name.partner_id.id ref_label = f'{apr.number} - {apr.detail_note or "-"}' @@ -1496,7 +1498,8 @@ class CreateReimburseCabWizard(models.TransientModel): raise UserError("Tidak ada rincian reimburse yang bisa dijurnalkan.") lines = [] - partner_id = request.user_id.partner_id.id + # partner_id = request.user_id.partner_id.id + partner_id = request.applicant_name.partner_id.id # 1. Buat Jurnal DEBIT dari setiap baris reimburse for line in request.reimburse_line_ids: -- cgit v1.2.3 From 255268563d2b739c5bf4a756614b280067d67050 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 30 Oct 2025 16:01:06 +0700 Subject: (andri) fix label journal --- indoteknik_custom/models/advance_payment_request.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 66f0aac6..187db257 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -1500,7 +1500,7 @@ class CreateReimburseCabWizard(models.TransientModel): lines = [] # partner_id = request.user_id.partner_id.id partner_id = request.applicant_name.partner_id.id - + ref_label = f'{request.number} - {request.detail_note or "-"}' # 1. Buat Jurnal DEBIT dari setiap baris reimburse for line in request.reimburse_line_ids: if not line.account_id: @@ -1518,12 +1518,11 @@ class CreateReimburseCabWizard(models.TransientModel): lines.append((0, 0, { 'account_id': self.account_id.id, 'partner_id': partner_id, - 'name': f'Reimburse {request.number}', + 'name': ref_label, 'debit': 0, 'credit': request.grand_total_reimburse, })) - ref_label = f'{request.number} - {request.detail_note or "-"}' # 3. Buat Journal Entry move = self.env['account.move'].create({ -- cgit v1.2.3 From d5968a30eaf997b9eaa01f4630fda3f411eaa0b5 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 30 Oct 2025 16:51:21 +0700 Subject: (andri) add user AP --- .../models/advance_payment_request.py | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 187db257..a1e63403 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -159,7 +159,7 @@ class AdvancePaymentRequest(models.Model): self.nominal = self.grand_total_reimburse def _compute_is_current_user_ap(self): - ap_user_ids = [23, 9468] + ap_user_ids = [23, 9468, 16729] is_ap = self.env.user.id in ap_user_ids for line in self: line.is_current_user_ap = is_ap @@ -372,7 +372,7 @@ class AdvancePaymentRequest(models.Model): def action_view_journal_uangmuka(self): self.ensure_one() - ap_user_ids = [23, 9468] + ap_user_ids = [23, 9468, 16729] if self.env.user.id not in ap_user_ids: raise UserError('Hanya User AP yang dapat menggunakan fitur ini.') @@ -497,7 +497,7 @@ class AdvancePaymentRequest(models.Model): # jakarta_tz = pytz.timezone('Asia/Jakarta') # now = datetime.now(jakarta_tz).replace(tzinfo=None) - ap_user_ids = [23, 9468] + ap_user_ids = [23, 9468, 16729] if self.env.user.id not in ap_user_ids: raise UserError('Hanya User AP yang dapat menggunakan fitur ini.') @@ -572,7 +572,7 @@ class AdvancePaymentRequest(models.Model): ) elif rec.status == 'pengajuan2': - ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP + ap_user_ids = [23, 9468, 16729] # 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 @@ -614,7 +614,7 @@ class AdvancePaymentRequest(models.Model): def action_ap_only(self): self.ensure_one() - ap_user_ids = [23, 9468] # Ganti sesuai kebutuhan + ap_user_ids = [23, 9468, 16729] # Ganti sesuai kebutuhan if self.env.user.id not in ap_user_ids: raise UserError('Hanya User AP yang dapat menggunakan fitur ini.') @@ -855,7 +855,7 @@ class AdvancePaymentUsageLine(models.Model): ) def _compute_is_current_user_ap(self): - ap_user_ids = [23, 9468] + ap_user_ids = [23, 9468, 16729] is_ap = self.env.user.id in ap_user_ids for line in self: line.is_current_user_ap = is_ap @@ -875,7 +875,7 @@ class AdvancePaymentUsageLine(models.Model): @api.onchange('done_attachment') def _onchange_done_attachment(self): - ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP + ap_user_ids = [23, 9468, 16729] # 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 @@ -944,7 +944,7 @@ class ReimburseLine(models.Model): ) def _compute_is_current_user_ap(self): - ap_user_ids = [23, 9468] + ap_user_ids = [23, 9468, 16729] is_ap = self.env.user.id in ap_user_ids for line in self: line.is_current_user_ap = is_ap @@ -1081,13 +1081,13 @@ class AdvancePaymentSettlement(models.Model): ) def _compute_is_current_user_ap(self): - ap_user_ids = [23, 9468] + ap_user_ids = [23, 9468, 16729] is_ap = self.env.user.id in ap_user_ids for line in self: line.is_current_user_ap = is_ap def action_toggle_check_attachment(self): - ap_user_ids = [23, 9468] + ap_user_ids = [23, 9468, 16729] if self.env.user.id not in ap_user_ids: raise UserError('Hanya User AP yang dapat menggunakan tombol ini.') @@ -1121,7 +1121,7 @@ class AdvancePaymentSettlement(models.Model): def action_view_journal_uangmuka(self): self.ensure_one() - ap_user_ids = [23, 9468] + ap_user_ids = [23, 9468, 16729] if self.env.user.id not in ap_user_ids: raise UserError('Hanya User AP yang dapat menggunakan fitur ini.') @@ -1185,7 +1185,7 @@ class AdvancePaymentSettlement(models.Model): def action_cab(self): self.ensure_one() - ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP + ap_user_ids = [23, 9468, 16729] # 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: @@ -1311,7 +1311,7 @@ class AdvancePaymentSettlement(models.Model): ) elif rec.status == 'pengajuan2': - ap_user_ids = [23, 9468] # List user ID yang boleh approve sebagai Finance AP + ap_user_ids = [23, 9468, 16729] # 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 -- cgit v1.2.3 From 88251508d31e0d110dd79ceafe430a0480a10c2b Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 30 Oct 2025 17:34:26 +0700 Subject: (andri) fix note --- indoteknik_custom/models/advance_payment_request.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index a1e63403..c82c09ff 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -989,7 +989,7 @@ class AdvancePaymentSettlement(models.Model): 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) + related = fields.Char(string='Dok. Terkait', tracking=3) # pemberian_line_ids = fields.One2many( # 'advance.payment.settlement.line', 'realization_id', string='Rincian Pemberian' @@ -1008,7 +1008,13 @@ class AdvancePaymentSettlement(models.Model): # 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) + def _get_default_note_approval(self): + template = ( + "Demikian dokumen Realisasi Uang Muka ini saya buat, dengan ini saya meminta persetujuan dibawah atas hasil penggunaan uang muka yang saya gunakan untuk kebutuhan realisasi " + ) + return template + + note_approval = fields.Text(string='Note Persetujuan', tracking=3, default=_get_default_note_approval) name_approval_departement = fields.Char(string='Approval Departement') name_approval_ap = fields.Char(string='Approval AP') -- cgit v1.2.3 From 4bdebe2972657f95b4d40251121c08acef17ab8a Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 31 Oct 2025 10:13:52 +0700 Subject: big fux --- indoteknik_custom/models/sale_order.py | 2 - indoteknik_custom/models/stock_move.py | 117 ++++++++++++++++++++---------- indoteknik_custom/models/stock_picking.py | 15 ++-- 3 files changed, 89 insertions(+), 45 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index b8de1697..1eba2808 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2722,8 +2722,6 @@ class SaleOrder(models.Model): if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988): return True - - salesperson_id = self.user_id.id approver_id = user.id team_leader_id = self.team_id.user_id.id diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 43c4676e..cac88287 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -186,9 +186,19 @@ class StockMoveLine(models.Model): line_no = fields.Integer('No', default=0) note = fields.Char('Note') manufacture = fields.Many2one('x_manufactures', string="Brands", related="product_id.x_manufacture", store=True) - outstanding_qty = fields.Float( - string='Outstanding Qty', - compute='_compute_delivery_line_status', + qty_yang_mau_dikirim = fields.Float( + string='Qty yang Mau Dikirim', + compute='_compute_delivery_status_detail', + store=False + ) + qty_terkirim = fields.Float( + string='Qty Terkirim', + compute='_compute_delivery_status_detail', + store=False + ) + qty_gantung = fields.Float( + string='Qty Gantung', + compute='_compute_delivery_status_detail', store=False ) delivery_status = fields.Selection([ @@ -196,47 +206,80 @@ class StockMoveLine(models.Model): ('partial', 'Partial'), ('partial_final', 'Partial Final'), ('full', 'Full'), - ], string='Delivery Status', compute='_compute_delivery_line_status', store=False) + ], string='Delivery Status', compute='_compute_delivery_status_detail', store=False) @api.depends('qty_done', 'product_uom_qty', 'picking_id.state') - def _compute_delivery_line_status(self): - for line in self: - line.outstanding_qty = 0.0 - line.delivery_status = 'none' - - picking = line.picking_id - if not picking or picking.picking_type_id.code != 'outgoing': + def _compute_delivery_status_detail(self): + for picking in self: + # Default values + picking.qty_yang_mau_dikirim = 0.0 + picking.qty_terkirim = 0.0 + picking.qty_gantung = 0.0 + picking.delivery_status = 'none' + + # Hanya berlaku untuk pengiriman (BU/OUT) + if picking.picking_id.picking_type_id.code != 'outgoing': continue - total_qty = line.move_id.product_uom_qty or 0 - done_qty = line.qty_done or 0 - - line.outstanding_qty = max(total_qty - done_qty, 0) + if picking.picking_id.name not in ['BU/OUT']: + continue - if total_qty == 0: + move_lines = picking + if not move_lines: continue - if done_qty == 0: - line.delivery_status = 'none' - elif done_qty > 0: - has_other_out = 0 - if picking.group_id and isinstance(picking.id, int): - has_other_out = self.env['stock.picking'].search_count([ - ('group_id', '=', picking.group_id.id), - ('name', 'ilike', 'BU/OUT'), - ('id', '!=', picking.id), - ('state', '=', 'done'), - ]) - if has_other_out and done_qty == total_qty: - line.delivery_status = 'partial_final' - elif not has_other_out and done_qty >= total_qty: - line.delivery_status = 'full' - elif has_other_out and done_qty < total_qty: - line.delivery_status = 'partial' - elif done_qty < total_qty: - line.delivery_status = 'partial' - else: - line.delivery_status = 'none' + # ====================== + # HITUNG QTY + # ====================== + total_qty = move_lines.product_uom_qty + + done_qty_total = move_lines.move_id.sale_line_id.qty_delivered + order_qty_total = move_lines.move_id.sale_line_id.product_uom_qty + gantung_qty_total = order_qty_total - done_qty_total - total_qty + + picking.qty_yang_mau_dikirim = total_qty + picking.qty_terkirim = done_qty_total + picking.qty_gantung = gantung_qty_total + + # if total_qty == 0: + # picking.delivery_status = 'none' + # continue + + # if done_qty_total == 0: + # picking.delivery_status = 'none' + # continue + + # ====================== + # CEK BU/OUT LAIN (BACKORDER) + # ====================== + # has_other_out = self.env['stock.picking'].search_count([ + # ('group_id', '=', picking.group_id.id), + # ('name', 'ilike', 'BU/OUT'), + # ('id', '!=', picking.id), + # ('state', 'in', ['assigned', 'waiting', 'confirmed', 'done']), + # ]) + + # ====================== + # LOGIKA STATUS + # ====================== + if gantung_qty_total == 0 and done_qty_total == 0: + # Semua barang udah terkirim, ga ada picking lain + picking.delivery_status = 'full' + + elif gantung_qty_total > 0 and total_qty > 0 and done_qty_total == 0: + # Masih ada picking lain dan sisa gantung → proses masih jalan + picking.delivery_status = 'partial' + + # elif gantung_qty_total > 0: + # # Ini picking terakhir, tapi qty belum full + # picking.delivery_status = 'partial_final' + + elif gantung_qty_total == 0 and done_qty_total > 0 and total_qty > 0: + # Udah kirim semua tapi masih ada picking lain (rare case) + picking.delivery_status = 'partial_final' + + else: + picking.delivery_status = 'none' # Ambil uom dari stock move @api.model diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7f8523a3..273fda82 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -212,6 +212,9 @@ class StockPicking(models.Model): if picking.picking_type_id.code != 'outgoing': continue + if picking.name not in ['BU/OUT']: + continue + move_lines = picking.move_line_ids_without_package if not move_lines: continue @@ -240,12 +243,12 @@ class StockPicking(models.Model): # ====================== # CEK BU/OUT LAIN (BACKORDER) # ====================== - has_other_out = self.env['stock.picking'].search_count([ - ('group_id', '=', picking.group_id.id), - ('name', 'ilike', 'BU/OUT'), - ('id', '!=', picking.id), - ('state', 'in', ['assigned', 'waiting', 'confirmed', 'done']), - ]) + # has_other_out = self.env['stock.picking'].search_count([ + # ('group_id', '=', picking.group_id.id), + # ('name', 'ilike', 'BU/OUT'), + # ('id', '!=', picking.id), + # ('state', 'in', ['assigned', 'waiting', 'confirmed', 'done']), + # ]) # ====================== # LOGIKA STATUS -- cgit v1.2.3 From c1640e45c37e6ab67cd90e2dbc415989a8a4ad45 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 31 Oct 2025 10:17:45 +0700 Subject: push --- indoteknik_custom/models/stock_move.py | 117 +++++++++++---------------------- 1 file changed, 37 insertions(+), 80 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index cac88287..43c4676e 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -186,19 +186,9 @@ class StockMoveLine(models.Model): line_no = fields.Integer('No', default=0) note = fields.Char('Note') manufacture = fields.Many2one('x_manufactures', string="Brands", related="product_id.x_manufacture", store=True) - qty_yang_mau_dikirim = fields.Float( - string='Qty yang Mau Dikirim', - compute='_compute_delivery_status_detail', - store=False - ) - qty_terkirim = fields.Float( - string='Qty Terkirim', - compute='_compute_delivery_status_detail', - store=False - ) - qty_gantung = fields.Float( - string='Qty Gantung', - compute='_compute_delivery_status_detail', + outstanding_qty = fields.Float( + string='Outstanding Qty', + compute='_compute_delivery_line_status', store=False ) delivery_status = fields.Selection([ @@ -206,80 +196,47 @@ class StockMoveLine(models.Model): ('partial', 'Partial'), ('partial_final', 'Partial Final'), ('full', 'Full'), - ], string='Delivery Status', compute='_compute_delivery_status_detail', store=False) + ], string='Delivery Status', compute='_compute_delivery_line_status', store=False) @api.depends('qty_done', 'product_uom_qty', 'picking_id.state') - def _compute_delivery_status_detail(self): - for picking in self: - # Default values - picking.qty_yang_mau_dikirim = 0.0 - picking.qty_terkirim = 0.0 - picking.qty_gantung = 0.0 - picking.delivery_status = 'none' - - # Hanya berlaku untuk pengiriman (BU/OUT) - if picking.picking_id.picking_type_id.code != 'outgoing': - continue + def _compute_delivery_line_status(self): + for line in self: + line.outstanding_qty = 0.0 + line.delivery_status = 'none' - if picking.picking_id.name not in ['BU/OUT']: + picking = line.picking_id + if not picking or picking.picking_type_id.code != 'outgoing': continue - move_lines = picking - if not move_lines: - continue + total_qty = line.move_id.product_uom_qty or 0 + done_qty = line.qty_done or 0 - # ====================== - # HITUNG QTY - # ====================== - total_qty = move_lines.product_uom_qty - - done_qty_total = move_lines.move_id.sale_line_id.qty_delivered - order_qty_total = move_lines.move_id.sale_line_id.product_uom_qty - gantung_qty_total = order_qty_total - done_qty_total - total_qty - - picking.qty_yang_mau_dikirim = total_qty - picking.qty_terkirim = done_qty_total - picking.qty_gantung = gantung_qty_total - - # if total_qty == 0: - # picking.delivery_status = 'none' - # continue - - # if done_qty_total == 0: - # picking.delivery_status = 'none' - # continue - - # ====================== - # CEK BU/OUT LAIN (BACKORDER) - # ====================== - # has_other_out = self.env['stock.picking'].search_count([ - # ('group_id', '=', picking.group_id.id), - # ('name', 'ilike', 'BU/OUT'), - # ('id', '!=', picking.id), - # ('state', 'in', ['assigned', 'waiting', 'confirmed', 'done']), - # ]) - - # ====================== - # LOGIKA STATUS - # ====================== - if gantung_qty_total == 0 and done_qty_total == 0: - # Semua barang udah terkirim, ga ada picking lain - picking.delivery_status = 'full' - - elif gantung_qty_total > 0 and total_qty > 0 and done_qty_total == 0: - # Masih ada picking lain dan sisa gantung → proses masih jalan - picking.delivery_status = 'partial' - - # elif gantung_qty_total > 0: - # # Ini picking terakhir, tapi qty belum full - # picking.delivery_status = 'partial_final' - - elif gantung_qty_total == 0 and done_qty_total > 0 and total_qty > 0: - # Udah kirim semua tapi masih ada picking lain (rare case) - picking.delivery_status = 'partial_final' + line.outstanding_qty = max(total_qty - done_qty, 0) - else: - picking.delivery_status = 'none' + if total_qty == 0: + continue + + if done_qty == 0: + line.delivery_status = 'none' + elif done_qty > 0: + has_other_out = 0 + if picking.group_id and isinstance(picking.id, int): + has_other_out = self.env['stock.picking'].search_count([ + ('group_id', '=', picking.group_id.id), + ('name', 'ilike', 'BU/OUT'), + ('id', '!=', picking.id), + ('state', '=', 'done'), + ]) + if has_other_out and done_qty == total_qty: + line.delivery_status = 'partial_final' + elif not has_other_out and done_qty >= total_qty: + line.delivery_status = 'full' + elif has_other_out and done_qty < total_qty: + line.delivery_status = 'partial' + elif done_qty < total_qty: + line.delivery_status = 'partial' + else: + line.delivery_status = 'none' # Ambil uom dari stock move @api.model -- cgit v1.2.3 From 47b3591071225983cfec5d76497efe5cceae0af6 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 31 Oct 2025 10:19:09 +0700 Subject: push --- indoteknik_custom/models/stock_move.py | 117 ++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 37 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 43c4676e..cac88287 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -186,9 +186,19 @@ class StockMoveLine(models.Model): line_no = fields.Integer('No', default=0) note = fields.Char('Note') manufacture = fields.Many2one('x_manufactures', string="Brands", related="product_id.x_manufacture", store=True) - outstanding_qty = fields.Float( - string='Outstanding Qty', - compute='_compute_delivery_line_status', + qty_yang_mau_dikirim = fields.Float( + string='Qty yang Mau Dikirim', + compute='_compute_delivery_status_detail', + store=False + ) + qty_terkirim = fields.Float( + string='Qty Terkirim', + compute='_compute_delivery_status_detail', + store=False + ) + qty_gantung = fields.Float( + string='Qty Gantung', + compute='_compute_delivery_status_detail', store=False ) delivery_status = fields.Selection([ @@ -196,47 +206,80 @@ class StockMoveLine(models.Model): ('partial', 'Partial'), ('partial_final', 'Partial Final'), ('full', 'Full'), - ], string='Delivery Status', compute='_compute_delivery_line_status', store=False) + ], string='Delivery Status', compute='_compute_delivery_status_detail', store=False) @api.depends('qty_done', 'product_uom_qty', 'picking_id.state') - def _compute_delivery_line_status(self): - for line in self: - line.outstanding_qty = 0.0 - line.delivery_status = 'none' - - picking = line.picking_id - if not picking or picking.picking_type_id.code != 'outgoing': + def _compute_delivery_status_detail(self): + for picking in self: + # Default values + picking.qty_yang_mau_dikirim = 0.0 + picking.qty_terkirim = 0.0 + picking.qty_gantung = 0.0 + picking.delivery_status = 'none' + + # Hanya berlaku untuk pengiriman (BU/OUT) + if picking.picking_id.picking_type_id.code != 'outgoing': continue - total_qty = line.move_id.product_uom_qty or 0 - done_qty = line.qty_done or 0 - - line.outstanding_qty = max(total_qty - done_qty, 0) + if picking.picking_id.name not in ['BU/OUT']: + continue - if total_qty == 0: + move_lines = picking + if not move_lines: continue - if done_qty == 0: - line.delivery_status = 'none' - elif done_qty > 0: - has_other_out = 0 - if picking.group_id and isinstance(picking.id, int): - has_other_out = self.env['stock.picking'].search_count([ - ('group_id', '=', picking.group_id.id), - ('name', 'ilike', 'BU/OUT'), - ('id', '!=', picking.id), - ('state', '=', 'done'), - ]) - if has_other_out and done_qty == total_qty: - line.delivery_status = 'partial_final' - elif not has_other_out and done_qty >= total_qty: - line.delivery_status = 'full' - elif has_other_out and done_qty < total_qty: - line.delivery_status = 'partial' - elif done_qty < total_qty: - line.delivery_status = 'partial' - else: - line.delivery_status = 'none' + # ====================== + # HITUNG QTY + # ====================== + total_qty = move_lines.product_uom_qty + + done_qty_total = move_lines.move_id.sale_line_id.qty_delivered + order_qty_total = move_lines.move_id.sale_line_id.product_uom_qty + gantung_qty_total = order_qty_total - done_qty_total - total_qty + + picking.qty_yang_mau_dikirim = total_qty + picking.qty_terkirim = done_qty_total + picking.qty_gantung = gantung_qty_total + + # if total_qty == 0: + # picking.delivery_status = 'none' + # continue + + # if done_qty_total == 0: + # picking.delivery_status = 'none' + # continue + + # ====================== + # CEK BU/OUT LAIN (BACKORDER) + # ====================== + # has_other_out = self.env['stock.picking'].search_count([ + # ('group_id', '=', picking.group_id.id), + # ('name', 'ilike', 'BU/OUT'), + # ('id', '!=', picking.id), + # ('state', 'in', ['assigned', 'waiting', 'confirmed', 'done']), + # ]) + + # ====================== + # LOGIKA STATUS + # ====================== + if gantung_qty_total == 0 and done_qty_total == 0: + # Semua barang udah terkirim, ga ada picking lain + picking.delivery_status = 'full' + + elif gantung_qty_total > 0 and total_qty > 0 and done_qty_total == 0: + # Masih ada picking lain dan sisa gantung → proses masih jalan + picking.delivery_status = 'partial' + + # elif gantung_qty_total > 0: + # # Ini picking terakhir, tapi qty belum full + # picking.delivery_status = 'partial_final' + + elif gantung_qty_total == 0 and done_qty_total > 0 and total_qty > 0: + # Udah kirim semua tapi masih ada picking lain (rare case) + picking.delivery_status = 'partial_final' + + else: + picking.delivery_status = 'none' # Ambil uom dari stock move @api.model -- cgit v1.2.3 From d50ce5c3553b97971debedcbb6fe896d99d92081 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 31 Oct 2025 15:45:13 +0700 Subject: balikin --- indoteknik_custom/models/stock_move.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index cac88287..38cf0199 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -22,12 +22,12 @@ class StockMove(models.Model): partial = fields.Boolean('Partial?', default=False) # Ambil product uom dari SO line - @api.model - def create(self, vals): - if vals.get('sale_line_id'): - sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) - vals['product_uom'] = sale_line.product_uom.id - return super().create(vals) + # @api.model + # def create(self, vals): + # if vals.get('sale_line_id'): + # sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) + # vals['product_uom'] = sale_line.product_uom.id + # return super().create(vals) # @api.model_create_multi # def create(self, vals_list): @@ -282,10 +282,10 @@ class StockMoveLine(models.Model): picking.delivery_status = 'none' # Ambil uom dari stock move - @api.model - def create(self, vals): - if 'move_id' in vals and 'product_uom_id' not in vals: - move = self.env['stock.move'].browse(vals['move_id']) - if move.product_uom: - vals['product_uom_id'] = move.product_uom.id - return super().create(vals) \ No newline at end of file + # @api.model + # def create(self, vals): + # if 'move_id' in vals and 'product_uom_id' not in vals: + # move = self.env['stock.move'].browse(vals['move_id']) + # if move.product_uom: + # vals['product_uom_id'] = move.product_uom.id + # return super().create(vals) \ No newline at end of file -- cgit v1.2.3 From c933cb57a0dbea8ba062a251f28f0a72eb4bb080 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Sat, 1 Nov 2025 07:46:02 +0700 Subject: (andri) it --- indoteknik_custom/models/advance_payment_request.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index c82c09ff..a3a3d20a 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -373,7 +373,8 @@ class AdvancePaymentRequest(models.Model): self.ensure_one() ap_user_ids = [23, 9468, 16729] - if self.env.user.id not in ap_user_ids: + is_it = self.env.user.has_group('indoteknik_custom.group_role_it') + if self.env.user.id not in ap_user_ids and not is_it: raise UserError('Hanya User AP yang dapat menggunakan fitur ini.') if not self.move_id: @@ -1127,8 +1128,9 @@ class AdvancePaymentSettlement(models.Model): def action_view_journal_uangmuka(self): self.ensure_one() - ap_user_ids = [23, 9468, 16729] - if self.env.user.id not in ap_user_ids: + ap_user_ids = [23, 9468, 16729] + is_it = self.env.user.has_group('indoteknik_custom.group_role_it') + if self.env.user.id not in ap_user_ids and not is_it: raise UserError('Hanya User AP yang dapat menggunakan fitur ini.') if not self.move_id: -- cgit v1.2.3 From c0380dd9b5ab6bf6a9538b9fa0c08082095c95fc Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Sat, 1 Nov 2025 09:17:32 +0700 Subject: refudn cancel walaupun udh approved --- indoteknik_custom/models/refund_sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index e6547a88..9eae5b32 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -989,7 +989,7 @@ class RefundSaleOrder(models.Model): for rec in self: if self.env.uid not in allowed_user_ids and not is_fat: raise UserError("❌ Hanya user yang bersangkutan atau Finance (FAT) yang bisa melakukan penolakan.") - if rec.status not in ['refund', 'reject']: + if rec.status != 'reject': rec.status = 'reject' rec.status_payment = 'reject' -- cgit v1.2.3 From 3c552beb2a8bbe51fecff96d3221a4ee1dedbe8f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 1 Nov 2025 13:16:19 +0700 Subject: remove write validation --- indoteknik_custom/models/tukar_guling_po.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 2a5ca3dd..739898a1 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -366,8 +366,8 @@ class TukarGulingPO(models.Model): # if bu_put: # raise UserError("❌ Tidak bisa retur BU/INPUT karena BU/PUT sudah Done!") - if self.operations.picking_type_id.id == 28 and tipe == 'tukar_guling': - raise UserError("❌ BU/INPUT tidak boleh di retur tukar guling") + # if self.operations.picking_type_id.id == 28 and tipe == 'tukar_guling': + # raise UserError("❌ BU/INPUT tidak boleh di retur tukar guling") # if self.operations.picking_type_id.id != 28: # if self._is_already_returned(self.operations): -- cgit v1.2.3 From d636c09592c484fddd436e6881c5052fd06c028a Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 3 Nov 2025 09:00:23 +0700 Subject: add so num in transfer --- indoteknik_custom/models/stock_picking.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 273fda82..c17fdbd5 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -198,6 +198,14 @@ class StockPicking(models.Model): ('partial_final', 'Partial Final'), ('full', 'Full'), ], string='Delivery Status', compute='_compute_delivery_status_detail', store=False) + so_num = fields.Char('SO Number', compute='_get_so_num') + + @api.depends('group_id') + def _get_so_num(self): + for record in self: + record.so_num = record.group_id.name + + @api.depends('move_line_ids_without_package.qty_done', 'move_line_ids_without_package.product_uom_qty', 'state') def _compute_delivery_status_detail(self): -- cgit v1.2.3 From 215411bc6cf4413941f978e4bf4d2f0658f8bd58 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 3 Nov 2025 12:06:14 +0700 Subject: uom coretax id --- indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/uom_uom.py | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 indoteknik_custom/models/uom_uom.py (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 165dae4e..6e1ef2e0 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -163,3 +163,4 @@ from . import letter_receivable from . import sj_tele from . import partial_delivery from . import domain_apo +from . import uom_uom \ No newline at end of file diff --git a/indoteknik_custom/models/uom_uom.py b/indoteknik_custom/models/uom_uom.py new file mode 100644 index 00000000..32e53d73 --- /dev/null +++ b/indoteknik_custom/models/uom_uom.py @@ -0,0 +1,6 @@ +from odoo import fields, models, api, _ + +class Uom(models.Model): + _inherit = 'uom.uom' + + coretax_id = fields.Char(string='Coretax ID') -- cgit v1.2.3 From 0bb7e8ff5eafa5dbc218d547999876bb11e05c69 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 3 Nov 2025 16:14:12 +0700 Subject: push --- indoteknik_custom/models/purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 534d8122..c2b15078 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1123,7 +1123,7 @@ class PurchaseOrder(models.Model): if not self.not_update_purchasepricelist: self.add_product_to_pricelist() for line in self.order_line: - if not line.product_id.public_categ_ids: + if line.product_id.type == 'product' and not line.product_id.categ_id: raise UserError("Product %s kategorinya kosong" % line.product_id.name) if not line.product_id.purchase_ok: raise UserError("Terdapat barang yang tidak bisa diproses") -- cgit v1.2.3 From d3626a4773647ef1910b66fcc8c981dcab562c65 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 4 Nov 2025 15:20:18 +0700 Subject: (andri) add cbd lock date & fix seq PUM reimburse --- indoteknik_custom/models/res_partner.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index ef1a5cf4..f80bbcb6 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -182,6 +182,7 @@ class ResPartner(models.Model): payment_history_url = fields.Text(string='Payment History URL') is_cbd_locked = fields.Boolean("Locked to CBD?", default=False, tracking=True, help="Jika dicentang, maka partner ini terkunci pada payment term CBD karena memiliki invoice yang sudah jatuh tempo lebih dari 30 hari.") + cbd_lock_date = fields.Datetime("CBD Lock Date", tracking=True, help="Tanggal ketika partner ini dikunci pada payment term CBD.") @api.model -- cgit v1.2.3 From cabe184a38a6a96b9557ff7debb2e9165894a7b6 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 5 Nov 2025 14:43:55 +0700 Subject: push --- indoteknik_custom/models/sale_order.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 1eba2808..d7b0ccee 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2342,11 +2342,6 @@ class SaleOrder(models.Model): # return self._create_notification_action('Notification', # 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') - if not order.with_context(ask_approval=True)._is_request_to_own_team_leader(): - return self._create_notification_action( - 'Peringatan', - 'Hanya bisa konfirmasi SO tim Anda.' - ) if order._requires_approval_margin_leader(): order.approval_status = 'pengajuan2' return self._create_approval_notification('Pimpinan') @@ -2362,6 +2357,12 @@ class SaleOrder(models.Model): self.check_limit_so_to_invoice() order.approval_status = 'pengajuan1' return self._create_approval_notification('Team Sales') + + if not order.with_context(ask_approval=True)._is_request_to_own_team_leader(): + return self._create_notification_action( + 'Peringatan', + 'Hanya bisa konfirmasi SO tim Anda.' + ) raise UserError("Bisa langsung Confirm") @@ -2716,11 +2717,12 @@ class SaleOrder(models.Model): if user.is_leader or user.is_sales_manager: return True + if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988): + return True + if not self.env.context.get("ask_approval") and user.id in (3401, 20, 3988, 17340): # admin (fida, nabila, ninda) raise UserError("Yahaha gabisa confirm so, minta ke sales nya ajah") - if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988): - return True salesperson_id = self.user_id.id approver_id = user.id -- cgit v1.2.3 From 89fe1e885966d46b1e96829d3ecc1c009e0514b8 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 5 Nov 2025 18:15:14 +0700 Subject: disable show description for PO altama --- indoteknik_custom/models/purchase_order.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index c2b15078..b9211276 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -118,9 +118,18 @@ class PurchaseOrder(models.Model): show_description = fields.Boolean( string='Show Description', - default=True + compute='_compute_show_description' ) - + + @api.depends('show_description') + def _compute_show_description(self): + for order in self: + # Show desc false for altama only + if order.partner_id == 5571: + order.show_description = False + else: + order.show_description = True + @api.onchange('show_description') def onchange_show_description(self): if self.show_description == True: -- cgit v1.2.3 From 3ff3ddc67d4664871314462fb184a6fe369f3a63 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 5 Nov 2025 18:54:45 +0700 Subject: disable show description for PO altama --- indoteknik_custom/models/purchase_order.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index b9211276..8772dea9 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -118,17 +118,17 @@ class PurchaseOrder(models.Model): show_description = fields.Boolean( string='Show Description', - compute='_compute_show_description' + compute='_compute_show_description', + default=True ) - @api.depends('show_description') + @api.depends('partner_id') def _compute_show_description(self): for order in self: - # Show desc false for altama only - if order.partner_id == 5571: + if order.partner_id.id == 5571: order.show_description = False else: - order.show_description = True + order.show_description = order.show_description @api.onchange('show_description') def onchange_show_description(self): -- cgit v1.2.3 From ddc06e464075c316de8f236e0e037dd964a972e3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 6 Nov 2025 10:16:52 +0700 Subject: add new field --- indoteknik_custom/models/stock_picking.py | 44 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index c17fdbd5..04847be8 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -89,6 +89,7 @@ class StockPicking(models.Model): readonly=True, related="id", ) + sj_documentations = fields.One2many('stock.picking.sj.document','picking_id', string='Dokumentasi SJ (Multi)') sj_documentation = fields.Binary(string="Dokumentasi Surat Jalan") paket_documentation = fields.Binary(string="Dokumentasi Paket") dispatch_documentation = fields.Binary(string="Dokumentasi Dispatch") @@ -205,8 +206,6 @@ class StockPicking(models.Model): for record in self: record.so_num = record.group_id.name - - @api.depends('move_line_ids_without_package.qty_done', 'move_line_ids_without_package.product_uom_qty', 'state') def _compute_delivery_status_detail(self): for picking in self: @@ -468,7 +467,6 @@ class StockPicking(models.Model): except ValueError: return False - def action_get_kgx_pod(self, shipment=False): self.ensure_one() @@ -1189,7 +1187,8 @@ class StockPicking(models.Model): self.sale_id.date_doc_kirim = self.date_doc_kirim def action_assign(self): - if self.env.context.get('default_picking_type_id') and ('BU/INPUT' not in self.name or 'BU/PUT' not in self.name): + if self.env.context.get('default_picking_type_id') and ( + 'BU/INPUT' not in self.name or 'BU/PUT' not in self.name): pickings_to_assign = self.filtered( lambda p: not (p.sale_id and p.sale_id.hold_outgoing) ) @@ -1205,18 +1204,15 @@ class StockPicking(models.Model): return res - def ask_approval(self): # if self.env.user.is_accounting: # if self.env.user.is_accounting and self.location_id.id == 57 or self.location_id == 57 and self.approval_status in ['pengajuan1', ''] and 'BU/IU' in self.name and self.approval_status == 'pengajuan1': # raise UserError("Bisa langsung set ke approval logistik") if self.env.user.is_accounting and self.approval_status == "pengajuan2" and 'BU/IU' in self.name: raise UserError("Tidak perlu ask approval sudah approval logistik") - if self.env.user.is_logistic_approver and self.location_id.id == 57 or self.location_id== 57 and self.approval_status == 'pengajuan2' and 'BU/IU' in self.name: + if self.env.user.is_logistic_approver and self.location_id.id == 57 or self.location_id == 57 and self.approval_status == 'pengajuan2' and 'BU/IU' in self.name: raise UserError("Bisa langsung Validate") - - # for calendar distribute only # if self.is_internal_use: # stock_move_lines = self.env['stock.move.line'].search([ @@ -1239,7 +1235,6 @@ class StockPicking(models.Model): raise UserError("Qty tidak boleh 0") pick.approval_status = 'pengajuan1' - def ask_receipt_approval(self): if self.env.user.is_logistic_approver: raise UserError('Bisa langsung validate tanpa Ask Receipt') @@ -1405,7 +1400,7 @@ class StockPicking(models.Model): and self.create_date > threshold_datetime and not self.so_lama): raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) - + if 'BU/OUT/' in self.name: self.driver_departure_date = datetime.datetime.utcnow() @@ -1447,13 +1442,16 @@ class StockPicking(models.Model): # if self.is_internal_use and not self.env.user.is_logistic_approver and self.location_id.id == 57 and self.approval_status == 'pengajuan2': # raise UserError("Harus di Approve oleh Logistik") - if self.is_internal_use and self.approval_status in ['pengajuan1', '', False] and 'BU/IU' in self.name and self.is_bu_iu == True: + if self.is_internal_use and self.approval_status in ['pengajuan1', '', + False] and 'BU/IU' in self.name and self.is_bu_iu == True: raise UserError("Tidak Bisa Validate, set approval status ke approval logistik terlebih dahhulu") - if self.is_internal_use and not self.env.user.is_logistic_approver and self.approval_status in ['pengajuan2'] and self.is_bu_iu == True and 'BU/IU' in self.name: + if self.is_internal_use and not self.env.user.is_logistic_approver and self.approval_status in [ + 'pengajuan2'] and self.is_bu_iu == True and 'BU/IU' in self.name: raise UserError("Harus di Approve oleh Logistik") - if self.is_internal_use and not self.env.user.is_accounting and self.approval_status in ['pengajuan1', '', False] and self.is_bu_iu == False: + if self.is_internal_use and not self.env.user.is_accounting and self.approval_status in ['pengajuan1', '', + False] and self.is_bu_iu == False: raise UserError("Harus di Approve oleh Accounting") if self.picking_type_id.id == 28 and not self.env.user.is_logistic_approver: @@ -1479,7 +1477,6 @@ class StockPicking(models.Model): current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.date_reserved = current_time - # Validate Qty Demand Can't higher than Qty Product if self.location_dest_id.id == 58 and 'BU/INPUT/' in self.name: for move in self.move_ids_without_package: @@ -1880,7 +1877,8 @@ class StockPicking(models.Model): 'name': move_line.product_id.name, 'code': move_line.product_id.default_code, 'qty': move_line.qty_done, - 'image': self.env['ir.attachment'].api_image('product.template', 'image_128', move_line.product_id.product_tmpl_id.id), + 'image': self.env['ir.attachment'].api_image('product.template', 'image_128', + move_line.product_id.product_tmpl_id.id), }) response = { @@ -2246,7 +2244,6 @@ class CheckProduct(models.Model): _order = 'picking_id, id' _inherit = ['barcodes.barcode_events_mixin'] - picking_id = fields.Many2one( 'stock.picking', string='Picking Reference', @@ -2699,8 +2696,6 @@ class ScanKoli(models.Model): out_move.qty_done += qty_to_assign qty_koli -= qty_to_assign - - def _reset_qty_done_if_no_scan(self, picking_id): product_bu_pick = self.env['stock.move.line'].search([('picking_id', '=', picking_id)]) @@ -2759,4 +2754,15 @@ class WarningModalWizard(models.TransientModel): def action_continue(self): if self.picking_id: return self.picking_id.with_context(skip_koli_check=True).button_validate() - return {'type': 'ir.actions.act_window_close'} \ No newline at end of file + return {'type': 'ir.actions.act_window_close'} + + +class StockPickingSjDocument(models.Model): + _name = 'stock.picking.sj.document' + _description = 'Dokumentasi Surat Jalan (Multi)' + _order = 'sequence, id' + _rec_name = 'id' + + picking_id = fields.Many2one('stock.picking', required=True, ondelete='cascade') + image = fields.Binary('Gambar', required=True, attachment=True) + sequence = fields.Integer('Urutan', default=10) \ No newline at end of file -- cgit v1.2.3 From be3c90b805dec1e178aa8cd70c187c79e3dd8443 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 6 Nov 2025 10:39:21 +0700 Subject: cr ask approval admin --- indoteknik_custom/models/sale_order.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d7b0ccee..ff79761c 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2363,6 +2363,15 @@ class SaleOrder(models.Model): 'Peringatan', 'Hanya bisa konfirmasi SO tim Anda.' ) + if self.env.user.id in (3401, 20, 3988, 17340): + if self.total_percent_margin <= 15: + self._requires_approval_margin_leader() + self.approval_status = 'pengajuan2' + elif self.total_percent_margin < 18: + self._requires_approval_margin_manager() + self.approval_status = 'pengajuan1' + elif 18 <= self.total_percent_margin <= 24: + self._requires_approval_team_sales() raise UserError("Bisa langsung Confirm") -- cgit v1.2.3 From 73908f6f60b4c5f3d7b7cd3a1a3ab04495de2047 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 6 Nov 2025 13:38:58 +0700 Subject: cr ask approval admin --- indoteknik_custom/models/sale_order.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index ff79761c..73af811b 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2363,15 +2363,6 @@ class SaleOrder(models.Model): 'Peringatan', 'Hanya bisa konfirmasi SO tim Anda.' ) - if self.env.user.id in (3401, 20, 3988, 17340): - if self.total_percent_margin <= 15: - self._requires_approval_margin_leader() - self.approval_status = 'pengajuan2' - elif self.total_percent_margin < 18: - self._requires_approval_margin_manager() - self.approval_status = 'pengajuan1' - elif 18 <= self.total_percent_margin <= 24: - self._requires_approval_team_sales() raise UserError("Bisa langsung Confirm") @@ -2712,7 +2703,7 @@ class SaleOrder(models.Model): def _requires_approval_team_sales(self): return ( - 18 <= self.total_percent_margin <= 24 + self.total_percent_margin >= 18 and self.env.user.id not in [11, 9, 375] # Eko, Ade, Putra and not self.env.user.is_sales_manager and not self.env.user.is_leader -- cgit v1.2.3 From af30e94cff35eec4b71cb89fbe7a9a4f654480cb Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 6 Nov 2025 13:40:28 +0700 Subject: cr ask approval admin --- indoteknik_custom/models/sale_order.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index ff79761c..1d07a1ff 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2363,15 +2363,6 @@ class SaleOrder(models.Model): 'Peringatan', 'Hanya bisa konfirmasi SO tim Anda.' ) - if self.env.user.id in (3401, 20, 3988, 17340): - if self.total_percent_margin <= 15: - self._requires_approval_margin_leader() - self.approval_status = 'pengajuan2' - elif self.total_percent_margin < 18: - self._requires_approval_margin_manager() - self.approval_status = 'pengajuan1' - elif 18 <= self.total_percent_margin <= 24: - self._requires_approval_team_sales() raise UserError("Bisa langsung Confirm") @@ -2712,7 +2703,8 @@ class SaleOrder(models.Model): def _requires_approval_team_sales(self): return ( - 18 <= self.total_percent_margin <= 24 + # 18 <= self.total_percent_margin <= 24 + self.total_percent_margin >= 18 and self.env.user.id not in [11, 9, 375] # Eko, Ade, Putra and not self.env.user.is_sales_manager and not self.env.user.is_leader -- cgit v1.2.3 From dd51ce73e27cd7639b512a219e7bc68bb467b4d3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 6 Nov 2025 15:04:05 +0700 Subject: show desc false altama --- indoteknik_custom/models/automatic_purchase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index d9ec17f4..012d680a 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -239,7 +239,8 @@ class AutomaticPurchase(models.Model): 'picking_type_id': 28, 'date_order': current_time, 'from_apo': True, - 'note_description': 'Automatic PO' + 'note_description': 'Automatic PO', + 'show_description': False if vendor_id.id == 5571 else True, } new_po = self.env['purchase.order'].create(param_header) -- cgit v1.2.3 From 449a3f3a3acaeb0b950f57daee1067d70d483d90 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 6 Nov 2025 15:04:16 +0700 Subject: show desc false altama --- indoteknik_custom/models/purchase_order.py | 8 -------- 1 file changed, 8 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 8772dea9..4475e777 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -118,17 +118,9 @@ class PurchaseOrder(models.Model): show_description = fields.Boolean( string='Show Description', - compute='_compute_show_description', default=True ) - @api.depends('partner_id') - def _compute_show_description(self): - for order in self: - if order.partner_id.id == 5571: - order.show_description = False - else: - order.show_description = order.show_description @api.onchange('show_description') def onchange_show_description(self): -- cgit v1.2.3 From ee9706c1f34c88ca2cfc32dbe7a90c38731166b1 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 6 Nov 2025 15:12:45 +0700 Subject: show desc false altama --- indoteknik_custom/models/automatic_purchase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index 012d680a..ce0328c4 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -240,7 +240,7 @@ class AutomaticPurchase(models.Model): 'date_order': current_time, 'from_apo': True, 'note_description': 'Automatic PO', - 'show_description': False if vendor_id.id == 5571 else True, + 'show_description': False if vendor_id == 5571 else True, } new_po = self.env['purchase.order'].create(param_header) -- cgit v1.2.3 From 3f905516b4b0b6573ebdf67f18582f20b023f7f6 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 6 Nov 2025 16:32:57 +0700 Subject: update --- indoteknik_custom/models/sale_order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 1d07a1ff..31662fb4 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2597,7 +2597,7 @@ class SaleOrder(models.Model): order.approval_status = 'pengajuan1' return self._create_approval_notification('Sales Manager') elif order._requires_approval_team_sales(): - order.approval_status = 'pengajuan1' + # order.approval_status = 'pengajuan1' return self._create_approval_notification('Team Sales') order.approval_status = 'approved' @@ -2703,8 +2703,8 @@ class SaleOrder(models.Model): def _requires_approval_team_sales(self): return ( - # 18 <= self.total_percent_margin <= 24 - self.total_percent_margin >= 18 + 18 <= self.total_percent_margin <= 24 + # self.total_percent_margin >= 18 and self.env.user.id not in [11, 9, 375] # Eko, Ade, Putra and not self.env.user.is_sales_manager and not self.env.user.is_leader -- cgit v1.2.3 From 459e34a6f96ed12522036bb12da2e0488581c016 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 7 Nov 2025 13:16:34 +0700 Subject: CR nathan stock picking --- indoteknik_custom/models/sale_order.py | 1 + indoteknik_custom/models/stock_picking.py | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 31662fb4..ef6318da 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -398,6 +398,7 @@ class SaleOrder(models.Model): compute="_compute_partner_is_cbd_locked" ) internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True, help="Internal Notes dari contact utama customer.") + is_so_fiktif = fields.Boolean('SO Fiktif?') def action_open_partial_delivery_wizard(self): # raise UserError("Fitur ini sedang dalam pengembangan") diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index c17fdbd5..8fefcf81 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -199,14 +199,19 @@ class StockPicking(models.Model): ('full', 'Full'), ], string='Delivery Status', compute='_compute_delivery_status_detail', store=False) so_num = fields.Char('SO Number', compute='_get_so_num') + is_so_fiktif = fields.Boolean('SO Fiktif?', compute='_compute_is_so_fiktif') + + @api.depends('sale_id.is_so_fiktif') + def _compute_is_so_fiktif(self): + for picking in self: + picking.is_so_fiktif = picking.sale_id.is_so_fiktif if picking.sale_id else False + @api.depends('group_id') def _get_so_num(self): for record in self: record.so_num = record.group_id.name - - @api.depends('move_line_ids_without_package.qty_done', 'move_line_ids_without_package.product_uom_qty', 'state') def _compute_delivery_status_detail(self): for picking in self: @@ -1383,6 +1388,8 @@ class StockPicking(models.Model): group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])]) active_model = self.env.context.get('active_model') + if self.is_so_fiktif == True: + raise UserError("SO Fiktif tidak bisa di validate") if self.location_id.id == 47 and self.env.user.id not in users_in_group.mapped( 'id') and self.state_approve_md != 'done': self.state_approve_md = 'waiting' if self.state_approve_md != 'pending' else 'pending' @@ -1562,6 +1569,10 @@ class StockPicking(models.Model): elif self.tukar_guling_po_id: self.tukar_guling_po_id.update_doc_state() + user = self.env.user + if not user.has_group('indoteknik_custom.group_role_logistic'): + raise UserWarning('Validate hnaya bisa di lakukan oleh logistik') + return res def automatic_reserve_product(self): @@ -1732,6 +1743,13 @@ class StockPicking(models.Model): 'indoteknik_custom.group_role_logistic') and self.picking_type_code == 'outgoing': raise UserError("Button ini hanya untuk Logistik") + if len(self.check_product_lines) >= 1: + raise UserError("Tidak Bisa cancel karena sudah di check product") + + if not self.env.user.is_logistic_approver and not self.env.user.has_group('indoteknik_custom.group_role_logistic'): + if self.name and ('BU/PICK' in self.name or 'BU/OUT' in self.name or 'BU/ORT' in self.name or 'BU/SRT' in self.name): + raise UserError("Button ini hanya untuk Logistik") + res = super(StockPicking, self).action_cancel() return res -- cgit v1.2.3 From f0931f9b846f05a4debc967597bb54f91f9b640a Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 7 Nov 2025 14:53:25 +0700 Subject: get image from envio --- indoteknik_custom/models/stock_picking.py | 35 +++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 8fefcf81..798b4109 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -5,16 +5,16 @@ from collections import defaultdict from datetime import timedelta, datetime from datetime import timedelta, datetime as waktu from itertools import groupby -import pytz, requests, json, requests +import pytz, json from dateutil import parser import datetime import hmac import hashlib -import base64 import requests import time import logging import re +import base64 _logger = logging.getLogger(__name__) @@ -823,6 +823,37 @@ class StockPicking(models.Model): picking.envio_cod_value = data.get("cod_value", 0.0) picking.envio_cod_status = data.get("cod_status") + images_data = data.get('images', []) + for img in images_data: + image_url = img.get('image') + if image_url: + try: + # Download image from URL + img_response = requests.get(image_url) + img_response.raise_for_status() + + # Encode image to base64 + image_base64 = base64.b64encode(img_response.content) + + # Create attachment in Odoo + attachment = self.env['ir.attachment'].create({ + 'name': 'Envio Image', + 'type': 'binary', + 'datas': image_base64, + 'res_model': picking._name, + 'res_id': picking.id, + 'mimetype': 'image/png', + }) + + # Post log note with attachment + picking.message_post( + body="Image Envio", + attachment_ids=[attachment.id] + ) + + except Exception as e: + picking.message_post(body=f"Gagal ambil image Envio: {str(e)}") + # Menyimpan log terbaru logs = data.get("logs", []) if logs and isinstance(logs, list) and logs[0]: -- cgit v1.2.3 From e8812897c981a55130c452cc15600a4758ffcffb Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 7 Nov 2025 15:30:15 +0700 Subject: fix date approve --- indoteknik_custom/models/commision.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index a937e2d0..441dd54f 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -423,8 +423,8 @@ class CustomerCommision(models.Model): def action_confirm_customer_commision(self): jakarta_tz = pytz.timezone('Asia/Jakarta') now = datetime.now(jakarta_tz) - - now_naive = now.replace(tzinfo=None) + now_utc = now.astimezone(pytz.utc) + now_naive = now_utc.replace(tzinfo=None) if not self.status or self.status == 'draft': self.status = 'pengajuan1' -- cgit v1.2.3 From 2cc12f95a1fbc0de8018446cd5e23a35ac892494 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 7 Nov 2025 17:48:14 +0700 Subject: fix error cancel SO --- indoteknik_custom/models/stock_picking.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 798b4109..0178ac33 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1778,8 +1778,10 @@ class StockPicking(models.Model): raise UserError("Tidak Bisa cancel karena sudah di check product") if not self.env.user.is_logistic_approver and not self.env.user.has_group('indoteknik_custom.group_role_logistic'): - if self.name and ('BU/PICK' in self.name or 'BU/OUT' in self.name or 'BU/ORT' in self.name or 'BU/SRT' in self.name): - raise UserError("Button ini hanya untuk Logistik") + for picking in self: + if picking.name and ('BU/PICK' in picking.name or 'BU/OUT' in picking.name or 'BU/ORT' in picking.name or 'BU/SRT' in picking.name): + if picking.state not in ['cancel']: + raise UserError("Button ini hanya untuk Logistik") res = super(StockPicking, self).action_cancel() return res -- cgit v1.2.3 From bb7121ab8a7dbdfb104eefbeddc9fb6d4383054b Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Sat, 8 Nov 2025 11:00:09 +0700 Subject: initial commit for branch feature/komisi-sales-internal --- indoteknik_custom/models/__init__.py | 3 +- indoteknik_custom/models/commission_internal.py | 129 ++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 indoteknik_custom/models/commission_internal.py (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 6e1ef2e0..a14c766e 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -163,4 +163,5 @@ from . import letter_receivable from . import sj_tele from . import partial_delivery from . import domain_apo -from . import uom_uom \ No newline at end of file +from . import uom_uom +from . import commission_internal diff --git a/indoteknik_custom/models/commission_internal.py b/indoteknik_custom/models/commission_internal.py new file mode 100644 index 00000000..8a333bda --- /dev/null +++ b/indoteknik_custom/models/commission_internal.py @@ -0,0 +1,129 @@ +from odoo import models, api, fields +from odoo.exceptions import AccessError, UserError, ValidationError +from datetime import timedelta, date +import logging + +_logger = logging.getLogger(__name__) + + +class CommissionInternal(models.Model): + _name = 'commission.internal' + _description = 'Commission Internal' + _order = 'date_doc, id desc' + _inherit = ['mail.thread'] + _rec_name = 'number' + + number = fields.Char(string='Document No', index=True, copy=False, readonly=True) + date_doc = fields.Date(string='Document Date', required=True) + month = fields.Selection([ + ('01', 'January'), ('02', 'February'), ('03', 'March'), + ('04', 'April'), ('05', 'May'), ('06', 'June'), + ('07', 'July'), ('08', 'August'), ('09', 'September'), + ('10', 'October'), ('11', 'November'), ('12', 'December') + ], string="Commission Month") + year = fields.Selection([(str(y), str(y)) for y in range(2025, 2036)], string="Commission Year") + description = fields.Char(string='Description') + comment = fields.Char(string='Comment') + commission_internal_line = fields.One2many('commission.internal.line', 'commission_internal_id', string='Lines', + auto_join=True, order='account_move_id asc') + + @api.model + def create(self, vals): + vals['number'] = self.env['ir.sequence'].next_by_code('commission.internal') or '0' + result = super(CommissionInternal, self).create(vals) + return result + + def _get_commission_internal_bank_account_ids(self): + bank_ids = self.env['ir.config_parameter'].sudo().get_param('commission.internal.bank.account.id') + if not bank_ids: + return [] + return [int(x.strip()) for x in bank_ids.split(',') if x.strip().isdigit()] + + def _get_period_range(self, period_year, period_month): + """Return (date_start, date_end) using separate year and month fields.""" + self.ensure_one() # make sure it's called on a single record + + year_str = period_year or '' + month_str = period_month or '' + + # Validate both fields exist + if not (year_str.isdigit() and month_str.isdigit()): + return None, None + + year = int(year_str) + month = int(month_str) + + # First day of this month + dt_start = date(year, month, 1) + + # Compute first day of next month + if month == 12: + next_month = date(year + 1, 1, 1) + else: + next_month = date(year, month + 1, 1) + + # Last day = one day before next month's first day + dt_end = next_month - timedelta(days=1) + + return dt_start, dt_end + + def generate_commission_from_generate_ledger(self): + if self.commission_internal_line: + raise UserError('Harus hapus semua line jika ingin generate ulang') + if not self.month: + raise UserError('Commission Month harus diisi') + if not self.year: + raise UserError('Commission Year harus diisi') + + dt_start, dt_end = self._get_period_range(self.year, self.month) + + account_bank_ids = self._get_commission_internal_bank_account_ids() + query = [ + ('move_id.state', '=', 'posted'), + ('account_id', 'in', account_bank_ids), + ('date', '>=', dt_start), + ('date', '<=', dt_end) + ] + ledgers = self.env['account.move.line'].search(query) + count = 0 + for ledger in ledgers: + _logger.info("Read General Ledger Account Move Line %s" % ledger.id) + self.env['commission.internal.line'].create([{ + 'commission_internal_id': self.id, + 'date': ledger.date, + 'number': ledger.move_id.name, + 'account_move_id': ledger.move_id.id, + 'account_move_line_id': ledger.id, + 'account_id': ledger.account_id.id, + 'label': ledger.name, + 'debit': ledger.debit, + 'credit': ledger.credit, + 'balance': ledger.balance + }]) + count += 1 + _logger.info("Commission Internal Line generated %s" % count) + + +class CommissionInternalLine(models.Model): + _name = 'commission.internal.line' + _description = 'Line' + _order = 'number asc, id' + + commission_internal_id = fields.Many2one('commission.internal', string='Internal Ref', required=True, + ondelete='cascade', index=True, copy=False) + date = fields.Date(string='Date') + number = fields.Char(string='Number') + account_move_id = fields.Many2one('account.move', string='Account Move') + account_move_line_id = fields.Many2one('account.move.line', string='Account Move Line') + account_id = fields.Many2one('account.account', string='Account') + label = fields.Char(string='Label') + debit = fields.Float(string='Debit') + credit = fields.Float(string='Credit') + balance = fields.Float(string='Balance') + ongkir = fields.Float(string='Ongkir') + refund = fields.Float(string='Refund') + pph = fields.Float(string='PPh23') + others = fields.Float(string='Others') + linenetamt = fields.Float(string='Net Amount') + dpp = fields.Float(string='DPP') + status = fields.Char(string='Status') -- cgit v1.2.3 From 7efc5a1a9b0f8a8479a9fed03577a76bdba8af22 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 8 Nov 2025 19:27:23 +0700 Subject: sync real deliv and deliv addr to stock picking (pick & out) --- indoteknik_custom/models/sale_order.py | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index ef6318da..bc33e8e6 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -400,6 +400,44 @@ class SaleOrder(models.Model): internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True, help="Internal Notes dari contact utama customer.") is_so_fiktif = fields.Boolean('SO Fiktif?') + def action_set_shipping_id(self): + for rec in self: + bu_pick = self.env['stock.picking'].search([ + ('state', 'not in', ['cancel']), + ('picking_type_id', '=', 30), + ('sale_id', '=', rec.id), + ('linked_manual_bu_out', '=', False) + ]) + # bu_out = bu_pick_has_out.mapped('linked_manual_bu_out') + bu_out = self.env['stock.picking'].search([ + ('sale_id', '=', rec.id), + ('picking_type_id', '=', 29), + ('state', 'not in', ['cancel', 'done']) + ]) + bu_pick_has_out = self.env['stock.picking'].search([ + ('state', 'not in', ['cancel']), + ('picking_type_id', '=', 30), + ('sale_id', '=', rec.id), + ('linked_manual_bu_out.id', '=', bu_out.id), + ('linked_manual_bu_out.state', 'not in', ['done', 'cancel']) + ]) + for pick in bu_pick_has_out: + linked_out = pick.linked_manual_bu_out + if pick.real_shipping_id != rec.real_shipping_id or linked_out.partner_id != rec.partner_shipping_id: + pick.real_shipping_id = rec.real_shipping_id + pick.partner_id = rec.partner_shipping_id + linked_out.partner_id = rec.partner_shipping_id + linked_out.real_shipping_id = rec.real_shipping_id + _logger.info('Updated bu_pick [%s] and bu_out [%s]', pick.name, linked_out.name) + + for pick in bu_pick: + if pick.real_shipping_id != rec.real_shipping_id: + pick.real_shipping_id = rec.real_shipping_id + pick.partner_id = rec.partner_shipping_id + bu_out.partner_id = rec.partner_shipping_id + bu_out.real_shipping_id = rec.real_shipping_id + _logger.info('Updated bu_pick [%s] without bu_out', pick.name) + def action_open_partial_delivery_wizard(self): # raise UserError("Fitur ini sedang dalam pengembangan") self.ensure_one() @@ -3377,6 +3415,9 @@ class SaleOrder(models.Model): # if updated_vals: # line.write(updated_vals) + + if 'real_shipping_id' in vals: + self.action_set_shipping_id() return res def button_refund(self): -- cgit v1.2.3 From 1554163f079ad816279ddbf2a244327e4e0b89c1 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 8 Nov 2025 21:06:42 +0700 Subject: set uom faktur xml based on coretax id --- indoteknik_custom/models/coretax_fatur.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py index ce94306f..7e0de919 100644 --- a/indoteknik_custom/models/coretax_fatur.py +++ b/indoteknik_custom/models/coretax_fatur.py @@ -147,6 +147,8 @@ class CoretaxFaktur(models.Model): subtotal = line_price_subtotal quantity = line_quantity total_discount = round(line_discount, 2) + coretax_id = line.product_uom_id.coretax_id + uom_name = line.product_uom_id.name # Calculate other tax values otherTaxBase = round(subtotal * (11 / 12), 2) if subtotal else 0 @@ -159,7 +161,7 @@ class CoretaxFaktur(models.Model): ET.SubElement(good_service, 'Opt').text = 'A' ET.SubElement(good_service, 'Code').text = '000000' ET.SubElement(good_service, 'Name').text = line_name - ET.SubElement(good_service, 'Unit').text = 'UM.0018' + ET.SubElement(good_service, uom_name).text = coretax_id # ET.SubElement(good_service, 'Price').text = str(round(line_price_unit, 2)) if line_price_unit else '0' ET.SubElement(good_service, 'Price').text = str(price_per_unit) ET.SubElement(good_service, 'Qty').text = str(quantity) -- cgit v1.2.3 From 1e3ab6c5d7b34e302d07bdb303982c87aaf59123 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 10 Nov 2025 10:20:58 +0700 Subject: validasi check product bu input --- indoteknik_custom/models/stock_picking.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0178ac33..eb9d8eda 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1459,6 +1459,9 @@ class StockPicking(models.Model): if len(self.check_product_lines) == 0 and 'BU/PICK/' in self.name: raise UserError(_("Tidak ada Check Product! Harap periksa kembali.")) + if len(self.check_product_lines) == 0 and 'BU/INPUT/' in self.name: + raise UserError(_("Tidak ada Check Product! Harap periksa kembali.")) + if self.total_koli > self.total_so_koli: raise UserError(_("Total Koli (%s) dan Total SO Koli (%s) tidak sama! Harap periksa kembali.") % (self.total_koli, self.t1otal_so_koli)) -- cgit v1.2.3 From c15634a9fba8338c07e3933a893850b8962ce441 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 10 Nov 2025 11:26:49 +0700 Subject: (andri) fix --- indoteknik_custom/models/sale_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index bc33e8e6..37f8b9cc 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2317,7 +2317,7 @@ class SaleOrder(models.Model): raise UserError("Terdapat DUPLIKASI data pada Product {}".format(line.product_id.display_name)) def sale_order_approve(self): - # self.check_duplicate_product() + self.check_duplicate_product() self.check_product_bom() self.check_credit_limit() self.check_limit_so_to_invoice() @@ -2588,7 +2588,7 @@ class SaleOrder(models.Model): for order in self: order._validate_delivery_amt() order._validate_uniform_taxes() - # order.check_duplicate_product() + order.check_duplicate_product() order.check_product_bom() order.check_credit_limit() order.check_limit_so_to_invoice() -- cgit v1.2.3 From 40c863af895ecbcc4af7162c0184f017467bafeb Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 10 Nov 2025 14:56:58 +0700 Subject: (andri) open all jenis biaya realisasi --- indoteknik_custom/models/advance_payment_request.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index a3a3d20a..6ea1c160 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -846,9 +846,9 @@ class AdvancePaymentUsageLine(models.Model): attachment_filename_pdf = fields.Char(string='Filename PDF') account_id = fields.Many2one( - 'account.account', string='Jenis Biaya', tracking=3, - domain="[('id', 'in', [484, 486, 488, 506, 507, 625, 471, 519, 527, 528, 529, 530, 565])]" # ID Jenis Biaya yang dibutuhkan + 'account.account', string='Jenis Biaya', tracking=3 # ID Jenis Biaya yang dibutuhkan ) + # domain="[('id', 'in', [484, 486, 488, 506, 507, 625, 471, 519, 527, 528, 529, 530, 565])]" # ID Jenis Biaya yang dibutuhkan is_current_user_ap = fields.Boolean( string="Is Current User AP", -- cgit v1.2.3 From 43fd0cdc3bd0aab6146a29d4400eafbd792057f0 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 10 Nov 2025 15:08:15 +0700 Subject: show desc PO --- indoteknik_custom/models/automatic_purchase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index ce0328c4..4b0ce325 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -284,7 +284,8 @@ class AutomaticPurchase(models.Model): 'ending_price': line.last_price, 'taxes_id': [(6, 0, [line.taxes_id.id])] if line.taxes_id else False, 'so_line_id': sales_match.sale_line_id.id if sales_match else None, - 'so_id': sales_match.sale_id.id if sales_match else None + 'so_id': sales_match.sale_id.id if sales_match else None, + 'show_description': False if vendor_id == 5571 else True, } new_po_line = self.env['purchase.order.line'].create(param_line) line.current_po_id = new_po.id -- cgit v1.2.3 From 85ff81225eb7f5bf9fd82aee072f75abb15fdf51 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 11 Nov 2025 08:58:30 +0700 Subject: aktivin code ambil uom dari SO --- indoteknik_custom/models/stock_move.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 38cf0199..ff87f911 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -282,10 +282,10 @@ class StockMoveLine(models.Model): picking.delivery_status = 'none' # Ambil uom dari stock move - # @api.model - # def create(self, vals): - # if 'move_id' in vals and 'product_uom_id' not in vals: - # move = self.env['stock.move'].browse(vals['move_id']) - # if move.product_uom: - # vals['product_uom_id'] = move.product_uom.id - # return super().create(vals) \ No newline at end of file + @api.model + def create(self, vals): + if 'move_id' in vals and 'product_uom_id' not in vals: + move = self.env['stock.move'].browse(vals['move_id']) + if move.product_uom: + vals['product_uom_id'] = move.product_uom.id + return super().create(vals) \ No newline at end of file -- cgit v1.2.3 From 4497a43c6a65921902e611f6787341a180cc29f3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 11 Nov 2025 08:59:07 +0700 Subject: aktivin code ambil uom dari SO --- indoteknik_custom/models/stock_move.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index ff87f911..cac88287 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -22,12 +22,12 @@ class StockMove(models.Model): partial = fields.Boolean('Partial?', default=False) # Ambil product uom dari SO line - # @api.model - # def create(self, vals): - # if vals.get('sale_line_id'): - # sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) - # vals['product_uom'] = sale_line.product_uom.id - # return super().create(vals) + @api.model + def create(self, vals): + if vals.get('sale_line_id'): + sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) + vals['product_uom'] = sale_line.product_uom.id + return super().create(vals) # @api.model_create_multi # def create(self, vals_list): -- cgit v1.2.3 From ffe4551ea6eaf0fa97d1a72cbc56596b33825cee Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 11 Nov 2025 11:50:44 +0700 Subject: (andri) add log --- indoteknik_custom/models/sale_order.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 37f8b9cc..357d2395 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2395,6 +2395,7 @@ class SaleOrder(models.Model): self.check_credit_limit() self.check_limit_so_to_invoice() order.approval_status = 'pengajuan1' + order.message_post(body="Mengajukan approval ke Team Sales") return self._create_approval_notification('Team Sales') if not order.with_context(ask_approval=True)._is_request_to_own_team_leader(): @@ -2636,7 +2637,8 @@ class SaleOrder(models.Model): order.approval_status = 'pengajuan1' return self._create_approval_notification('Sales Manager') elif order._requires_approval_team_sales(): - # order.approval_status = 'pengajuan1' + order.approval_status = 'pengajuan1' + order.message_post(body="Mengajukan approval ke Team Sales") return self._create_approval_notification('Team Sales') order.approval_status = 'approved' -- cgit v1.2.3 From f1a8e2740bf42524be6dde351c694edb28786aef Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 11 Nov 2025 14:17:43 +0700 Subject: (andri) fix ask approval --- indoteknik_custom/models/sale_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 357d2395..7070717b 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2759,10 +2759,10 @@ class SaleOrder(models.Model): if user.is_leader or user.is_sales_manager: return True - if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988): + if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988, 17340): return True - if not self.env.context.get("ask_approval") and user.id in (3401, 20, 3988, 17340): # admin (fida, nabila, ninda) + if not self.env.context.get("ask_approval") and user.id in (3401, 20, 3988, 17340): # admin (fida, nabila, ninda, feby) raise UserError("Yahaha gabisa confirm so, minta ke sales nya ajah") -- cgit v1.2.3 From b74b5bc94ed7a84ca2387fc0373f25b5eb27593d Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 11 Nov 2025 14:43:00 +0700 Subject: (andri) fix usererror --- indoteknik_custom/models/sale_order.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 7070717b..1d34374e 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2763,8 +2763,7 @@ class SaleOrder(models.Model): return True if not self.env.context.get("ask_approval") and user.id in (3401, 20, 3988, 17340): # admin (fida, nabila, ninda, feby) - raise UserError("Yahaha gabisa confirm so, minta ke sales nya ajah") - + raise UserError("Sales Admin tidak bisa confirm SO, silahkan hubungi Salesperson yang bersangkutan.") salesperson_id = self.user_id.id approver_id = user.id -- cgit v1.2.3 From c1fec44860dae0d762959f1ab34617f6ef6e42cf Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 11 Nov 2025 15:38:54 +0700 Subject: (andri) fix ask approval for sales admin --- indoteknik_custom/models/sale_order.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 1d34374e..6b1057dd 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2404,6 +2404,13 @@ class SaleOrder(models.Model): 'Hanya bisa konfirmasi SO tim Anda.' ) + user = self.env.user + is_sales_admin = user.id in (3401, 20, 3988, 17340) + if is_sales_admin: + order.approval_status = 'pengajuan1' + order.message_post(body="Mengajukan approval ke Team Sales") + return self._create_approval_notification('Team Sales') + raise UserError("Bisa langsung Confirm") def send_notif_to_salesperson(self, cancel=False): -- cgit v1.2.3 From 704f27afd94787260543a2b6fed2ecfa13e786cb Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 11 Nov 2025 16:01:14 +0700 Subject: fix coretax xml --- indoteknik_custom/models/coretax_fatur.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py index 7e0de919..cabcd5d6 100644 --- a/indoteknik_custom/models/coretax_fatur.py +++ b/indoteknik_custom/models/coretax_fatur.py @@ -148,8 +148,6 @@ class CoretaxFaktur(models.Model): quantity = line_quantity total_discount = round(line_discount, 2) coretax_id = line.product_uom_id.coretax_id - uom_name = line.product_uom_id.name - # Calculate other tax values otherTaxBase = round(subtotal * (11 / 12), 2) if subtotal else 0 vat_amount = round(otherTaxBase * 0.12, 2) @@ -161,7 +159,7 @@ class CoretaxFaktur(models.Model): ET.SubElement(good_service, 'Opt').text = 'A' ET.SubElement(good_service, 'Code').text = '000000' ET.SubElement(good_service, 'Name').text = line_name - ET.SubElement(good_service, uom_name).text = coretax_id + ET.SubElement(good_service, 'Unit').text = coretax_id # ET.SubElement(good_service, 'Price').text = str(round(line_price_unit, 2)) if line_price_unit else '0' ET.SubElement(good_service, 'Price').text = str(price_per_unit) ET.SubElement(good_service, 'Qty').text = str(quantity) -- cgit v1.2.3 From 4274751a04b7fc02b38a0af4683f06ae4f5508ad Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 12 Nov 2025 08:41:10 +0700 Subject: fix notify --- indoteknik_custom/models/sale_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 6b1057dd..861089d5 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2395,8 +2395,8 @@ class SaleOrder(models.Model): self.check_credit_limit() self.check_limit_so_to_invoice() order.approval_status = 'pengajuan1' - order.message_post(body="Mengajukan approval ke Team Sales") - return self._create_approval_notification('Team Sales') + order.message_post(body="Mengajukan approval ke Sales") + return self._create_approval_notification('Sales') if not order.with_context(ask_approval=True)._is_request_to_own_team_leader(): return self._create_notification_action( -- cgit v1.2.3 From 1086aadae846f8334cdb0292c606fb5a4d186f89 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 12 Nov 2025 08:42:34 +0700 Subject: fix --- indoteknik_custom/models/sale_order.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 861089d5..2de4109d 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2395,8 +2395,8 @@ class SaleOrder(models.Model): self.check_credit_limit() self.check_limit_so_to_invoice() order.approval_status = 'pengajuan1' - order.message_post(body="Mengajukan approval ke Sales") - return self._create_approval_notification('Sales') + order.message_post(body="Mengajukan approval ke Team Sales") + return self._create_approval_notification('Team Sales') if not order.with_context(ask_approval=True)._is_request_to_own_team_leader(): return self._create_notification_action( @@ -2408,8 +2408,8 @@ class SaleOrder(models.Model): is_sales_admin = user.id in (3401, 20, 3988, 17340) if is_sales_admin: order.approval_status = 'pengajuan1' - order.message_post(body="Mengajukan approval ke Team Sales") - return self._create_approval_notification('Team Sales') + order.message_post(body="Mengajukan approval ke Sales") + return self._create_approval_notification('Sales') raise UserError("Bisa langsung Confirm") -- cgit v1.2.3 From 583a8e3064790a06063d5dc1b06ed8122bb22e4e Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 12 Nov 2025 08:44:27 +0700 Subject: (andri) fix --- indoteknik_custom/models/sale_order.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 2de4109d..8e731307 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2383,12 +2383,14 @@ class SaleOrder(models.Model): if order._requires_approval_margin_leader(): order.approval_status = 'pengajuan2' + order.message_post(body="Mengajukan approval ke Pimpinan") return self._create_approval_notification('Pimpinan') elif order._requires_approval_margin_manager(): self.check_product_bom() self.check_credit_limit() self.check_limit_so_to_invoice() order.approval_status = 'pengajuan1' + order.message_post(body="Mengajukan approval ke Sales Manager") return self._create_approval_notification('Sales Manager') elif order._requires_approval_team_sales(): self.check_product_bom() -- cgit v1.2.3 From 7049765e3fa5ffca23725d652134ab3662912762 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 12 Nov 2025 23:57:23 +0700 Subject: add tracking so fiktif --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8e731307..14b66d87 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -398,7 +398,7 @@ class SaleOrder(models.Model): compute="_compute_partner_is_cbd_locked" ) internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True, help="Internal Notes dari contact utama customer.") - is_so_fiktif = fields.Boolean('SO Fiktif?') + is_so_fiktif = fields.Boolean('SO Fiktif?', tracking=3) def action_set_shipping_id(self): for rec in self: -- cgit v1.2.3 From e1c3a9b0ab65a698eeb64b7ab17dd82558c273fa Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 12 Nov 2025 23:58:03 +0700 Subject: add tracking so fiktif --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 49001974..7c4cea8b 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -200,7 +200,7 @@ class StockPicking(models.Model): ('full', 'Full'), ], string='Delivery Status', compute='_compute_delivery_status_detail', store=False) so_num = fields.Char('SO Number', compute='_get_so_num') - is_so_fiktif = fields.Boolean('SO Fiktif?', compute='_compute_is_so_fiktif') + is_so_fiktif = fields.Boolean('SO Fiktif?', compute='_compute_is_so_fiktif', tracking=3) @api.depends('sale_id.is_so_fiktif') def _compute_is_so_fiktif(self): -- cgit v1.2.3 From 39c95b7097f188350d71414c72f6abe7a711cf18 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 13 Nov 2025 15:12:09 +0700 Subject: (andri) fix sync janji bayar --- indoteknik_custom/models/account_move.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 684ef335..c460a332 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -161,7 +161,8 @@ class AccountMove(models.Model): def action_sync_promise_date(self): self.ensure_one() finance_user_ids = [688] - if self.env.user.id not in finance_user_ids: + is_it = self.env.user.has_group('indoteknik_custom.group_role_it') + if self.env.user.id not in finance_user_ids and not is_it: raise UserError('Hanya Finance (Widya) yang dapat menggunakan fitur ini.') if not self.customer_promise_date: raise UserError("Isi Janji Bayar terlebih dahulu sebelum melakukan sinkronisasi.") @@ -169,10 +170,11 @@ class AccountMove(models.Model): other_invoices = self.env['account.move'].search([ ('id', '!=', self.id), ('partner_id', '=', self.partner_id.id), - ('invoice_date_due', '=', self.invoice_date_due), + ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), ('move_type', '=', 'out_invoice'), ('state', '=', 'posted'), - ('date_terima_tukar_faktur', '!=', False) + ('date_terima_tukar_faktur', '!=', False), + ('invoice_payment_term_id.name', 'ilike', 'tempo') ]) lines = [] for inv in other_invoices: -- cgit v1.2.3 From 37469d36fe1d03f3c497e8aba34bc6d81fff033f Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Thu, 13 Nov 2025 16:29:35 +0700 Subject: checkpoint calculate commission internal using helper --- indoteknik_custom/models/commission_internal.py | 233 ++++++++++++++++++++++++ 1 file changed, 233 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/commission_internal.py b/indoteknik_custom/models/commission_internal.py index 8a333bda..ce074bcc 100644 --- a/indoteknik_custom/models/commission_internal.py +++ b/indoteknik_custom/models/commission_internal.py @@ -26,6 +26,8 @@ class CommissionInternal(models.Model): comment = fields.Char(string='Comment') commission_internal_line = fields.One2many('commission.internal.line', 'commission_internal_id', string='Lines', auto_join=True, order='account_move_id asc') + commission_internal_result = fields.One2many('commission.internal.result', 'commission_internal_id', string='Result', + auto_join=True, order='account_move_id asc') @api.model def create(self, vals): @@ -67,6 +69,194 @@ class CommissionInternal(models.Model): return dt_start, dt_end + # CREDIT > 0 + def _calculate_exclude_credit(self): + query = [ + ('commission_internal_id.id', '=', self.id), + ('credit', '>', 0), + ('status', '=', False) + ] + lines = self.env['commission.internal.line'].search(query) + for line in lines: + line.helper1 = 'CREDIT' + + # INV/20 + def _calculate_keyword_invoice(self): + query = [ + ('commission_internal_id.id', '=', self.id), + ('helper1', '=', False), + ('label', 'ilike', '%INV/20%'), + ] + lines = self.env['commission.internal.line'].search(query) + # parse label and set helper + for line in lines: + line.helper1 = 'INV/20' + + # ONGKOS KIRIM SO/20 + def _calculate_keyword_deliveryamt(self): + query = [ + ('commission_internal_id.id', '=', self.id), + ('helper1', '=', False), + ('label', 'ilike', '%ONGKOS KIRIM SO/20%'), + ] + lines = self.env['commission.internal.line'].search(query) + for line in lines: + line.helper1 = 'ONGKOS KIRIM SO/20' + + # Payment SO/20 + def _calculate_keyword_payment(self): + query = [ + ('commission_internal_id.id', '=', self.id), + ('helper1', '=', False), + ('label', 'ilike', '%Payment SO/20%'), + ] + lines = self.env['commission.internal.line'].search(query) + for line in lines: + line.helper1 = 'Payment SO/20' + + # UANG MUKA PENJUALAN SO/20 + def _calculate_keyword_dp(self): + query = [ + ('commission_internal_id.id', '=', self.id), + ('helper1', '=', False), + ('label', 'ilike', '%UANG MUKA PENJUALAN SO/20%'), + ] + lines = self.env['commission.internal.line'].search(query) + for line in lines: + line.helper1 = 'UANG MUKA PENJUALAN SO/20' + + def _calculate_keyword_undefined(self): + query = [ + ('commission_internal_id.id', '=', self.id), + ('helper1', '=', False), + ] + lines = self.env['commission.internal.line'].search(query) + for line in lines: + line.helper1 = 'UNDEFINED' + + def _parse_label_helper2(self): + exception = ['CREDIT', 'UNDEFINED'] + query = [ + ('commission_internal_id.id', '=', self.id), + ('helper1', 'not in', exception) + ] + lines = self.env['commission.internal.line'].search(query) + for line in lines: + clean_label = line.label.replace('-', ' ').replace(',', ' ') + list_label = clean_label.split() + list_helper2 = [] + for key in list_label: + clean_key = key.replace(',', '') # delete commas for make sure + if clean_key[:6] == 'INV/20': + list_helper2.append(clean_key) + elif clean_key[:5] == 'SO/20': + list_invoice = self._switch_so_to_inv(clean_key) + str_invoice = ' '.join(list_invoice) + list_helper2.append(str_invoice) + result_helper2 = ' '.join(list_helper2) + line.helper2 = result_helper2 + + def _switch_so_to_inv(self, order): + list_state = ['sale', 'done'] + result = [] + query = [ + ('state', 'in', list_state), + ('name', '=', order) + ] + sales = self.env['sale.order'].search(query) + if sales: + for sale in sales: + if sale.invoice_ids: + for invoice in sale.invoice_ids: + if invoice.state == 'posted': + result.append(invoice.name) + else: + result.append(order) + else: + result.append(order) + return result + + # fill later TODO @stephan + def calculate_commission_internal_result(self): + query = [ + ('commission_internal_id.id', '=', self.id), + ('helper2', '!=', False) + ] + lines = self.env['commission.internal.line'].search(query) + for line in lines: + list_so = list_invoice = [] + list_key = line.helper2.split() + for key in list_key: + if key[:6] == 'INV/20': + list_invoice.append(key) + if key[:5] == 'SO/20': + list_so.append(key) + invoices = self.env['account.move'].search([('state', '=', 'posted'), ('name', 'in', list_invoice)]) + orders = self.env['sale.order'].search([('state', 'in', ['done', 'sale']), ('name', 'in', list_so)]) + invoice_data = invoices.mapped(lambda r: { + 'res_name': 'account.move', + 'res_id': r.id, + 'name': r.name, + 'date': r.invoice_date, + 'customer': r.partner_id.name, + 'salesperson': r.user_id.name, + 'amount_untaxed': r.amount_untaxed, + 'amount_tax': r.amount_tax, + 'amount_total': r.amount_total + }) + sale_data = orders.mapped(lambda r: { + 'res_name': 'sale.order', + 'res_id': r.id, + 'name': r.name, + 'date': r.date_order, + 'customer': r.partner_id.name, + 'salesperson': r.user_id.name, + 'amount_untaxed': r.amount_untaxed, + 'amount_tax': r.amount_tax, + 'amount_total': r.grand_total + }) + invoices_and_sales = invoice_data + sale_data + total_amount = sum(item.get('amount_total', 0.0) for item in invoices_and_sales) + for data in invoices_and_sales: + prorate = data.get('amount_total', 0.0) / total_amount + # net_amount_prorate = data.get('amount_total', 0.0) * prorate + net_amount_prorate = line.balance * prorate + self.env['commission.internal.result'].create([{ + 'commission_internal_id': line.commission_internal_id.id, + 'commission_internal_line_id': line.id, + 'date_doc': data['date'], + 'number': data['name'], + 'res_name': data['res_name'], + 'res_id': data['res_id'], + 'name': data['name'], + 'salesperson': data['salesperson'], + 'totalamt': data['amount_total'], + 'uang_masuk_line_id': line.account_move_line_id.id, + 'uang_masuk_id': line.account_move_id.id, + 'date_uang_masuk': line.date, + 'label_uang_masuk': line.label, + 'nomor_uang_masuk': line.number, + 'uang_masuk': line.balance, + 'linenetamt_prorate': net_amount_prorate, + 'helper1': line.helper1 + }]) + print(1) + print(1) + + # this button / method works for train data in July 2025 + def calculate_commission_internal_from_keyword(self): + if not self.commission_internal_line: + raise UserError('Commission Internal Line kosong, click Copy GL terlebih dahulu') + # execute helper1 for mark keyword + self._calculate_exclude_credit() + self._calculate_keyword_invoice() + self._calculate_keyword_deliveryamt() + self._calculate_keyword_payment() + self._calculate_keyword_dp() + self._calculate_keyword_undefined() + # execute helper2 for parse the label into INV/ or SO/ and switch SO to INV if available + self._parse_label_helper2() + def generate_commission_from_generate_ledger(self): if self.commission_internal_line: raise UserError('Harus hapus semua line jika ingin generate ulang') @@ -127,3 +317,46 @@ class CommissionInternalLine(models.Model): linenetamt = fields.Float(string='Net Amount') dpp = fields.Float(string='DPP') status = fields.Char(string='Status') + salespersons = fields.Char(string='Salespersons') + invoices = fields.Char(string='Invoices') + helper1 = fields.Char(string='Helper1') + helper2 = fields.Char(string='Helper2') + helper3 = fields.Char(string='Helper3') + helper4 = fields.Char(string='Helper4') + helper5 = fields.Char(string='Helper5') + + +class CommissionInternalResult(models.Model): + _name = 'commission.internal.result' + _description = 'Result' + _order = 'number asc, id' + + commission_internal_id = fields.Many2one('commission.internal', string='Internal Ref', required=True, + ondelete='cascade', index=True, copy=False) + commission_internal_line_id = fields.Many2one('commission.internal.line', string='Line Ref') + res_name = fields.Char(string='Res Name') + res_id = fields.Integer(string='Res ID') + date_doc = fields.Date(string='Date Doc') + number = fields.Char(string='Number') + name = fields.Char(string='Name') + salesperson = fields.Char(string='Salesperson') + totalamt = fields.Float(string='Total Amount') + uang_masuk_line_id = fields.Many2one('account.move.line', string='Uang Masuk Line ID') + uang_masuk_id = fields.Many2one('account.move', string='Uang Masuk ID') + date_uang_masuk = fields.Date(string='Date Uang Masuk') + label_uang_masuk = fields.Char(string='Label Uang Masuk') + nomor_uang_masuk = fields.Char(string='Nomor Uang Masuk') + uang_masuk = fields.Float(string='Uang Masuk') + ongkir = fields.Float(string='Ongkir') + refund = fields.Float(string='Refund') + pph = fields.Float(string='PPh23') + others = fields.Float(string='Others') + linenetamt = fields.Float(string='Net Amount') + linenetamt_prorate = fields.Float(string='Net Amount Prorate') + dpp = fields.Float(string='DPP') + status = fields.Char(string='Status') + helper1 = fields.Char(string='Helper1') + helper2 = fields.Char(string='Helper2') + helper3 = fields.Char(string='Helper3') + helper4 = fields.Char(string='Helper4') + helper5 = fields.Char(string='Helper5') -- cgit v1.2.3 From ae89c43f6efa66ac7e332e62ce6cc6817f75c16d Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 13 Nov 2025 17:06:48 +0700 Subject: (andri) add invoice payment widget di tree inv, unlock COA reimburse, and add tracking edit pricelist contact --- indoteknik_custom/models/account_move.py | 27 +++++++++++++++++++++- .../models/advance_payment_request.py | 3 +-- indoteknik_custom/models/res_partner.py | 3 +++ 3 files changed, 30 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index c460a332..12e8ecba 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -3,7 +3,7 @@ from odoo.exceptions import AccessError, UserError, ValidationError from markupsafe import escape as html_escape from datetime import timedelta, date, datetime from pytz import timezone, utc -import logging +import logging, json import base64 import PyPDF2 import os @@ -108,6 +108,31 @@ class AccountMove(models.Model): ) internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True, help="Internal Notes dari contact utama customer.") + payment_info = fields.Text( + string="Payment Info", + compute='_compute_payment_info', + store=False, + help="Informasi pembayaran yang diambil dari payment yang sudah direkonsiliasi ke invoice ini." + ) + + def _compute_payment_info(self): + for rec in self: + summary = "" + try: + widget_data = rec.invoice_payments_widget + if widget_data: + data = json.loads(widget_data) + lines = [] + for item in data.get('content', []): + amount = item.get('amount', 0.0) + date = item.get('date') or item.get('payment_date') or '' + formatted_amount = formatLang(self.env, amount, currency_obj=rec.currency_id) + lines.append(f"Paid on {date} - {formatted_amount}") + summary = "\n".join(lines) if lines else (data.get('title', '') or "") + except Exception: + summary = "" + rec.payment_info = summary + # def _check_and_lock_cbd(self): # cbd_term = self.env['account.payment.term'].browse(26) # today = date.today() diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 6ea1c160..42097923 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -909,8 +909,7 @@ class ReimburseLine(models.Model): date = fields.Date(string='Tanggal', required=True, default=fields.Date.today) account_id = fields.Many2one( 'account.account', - string='Jenis Biaya', tracking=3, - domain="[('id', 'in', [484, 486, 527, 529, 530, 471, 473, 492, 493, 488, 625, 528, 533, 534])]" + string='Jenis Biaya', tracking=3 ) description = fields.Text(string='Description', required=True, tracking=3) distance_departure = fields.Float(string='Pergi (Km)', tracking=3) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index f80bbcb6..7f4feb75 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -200,6 +200,9 @@ class ResPartner(models.Model): string='Previous Payment Term' ) + property_product_pricelist = fields.Many2one( + tracking=True + ) @api.depends("street", "street2", "city", "state_id", "country_id", "blok", "nomor", "rt", "rw", "kelurahan_id", "kecamatan_id") -- cgit v1.2.3 From 83ceea85773615bf3f031554fa2ef9a2bc7c7760 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 14 Nov 2025 14:01:33 +0700 Subject: (andri) fix printout penutupan tempo --- indoteknik_custom/models/account_move.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 12e8ecba..5e38b544 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -32,7 +32,6 @@ class AccountMove(models.Model): new_due_date = fields.Date(string='New Due') counter = fields.Integer(string="Counter", default=0) cost_centre_id = fields.Many2one('cost.centre', string='Cost Centre') - analytic_account_ids = fields.Many2many('account.analytic.account', string='Analytic Account') due_line = fields.One2many('due.extension.line', 'invoice_id', compute='_compute_due_line', string='Due Extension Lines') no_faktur_pajak = fields.Char(string='No Faktur Pajak') date_completed = fields.Datetime(string='Date Completed') @@ -127,8 +126,8 @@ class AccountMove(models.Model): amount = item.get('amount', 0.0) date = item.get('date') or item.get('payment_date') or '' formatted_amount = formatLang(self.env, amount, currency_obj=rec.currency_id) - lines.append(f"Paid on {date} - {formatted_amount}") - summary = "\n".join(lines) if lines else (data.get('title', '') or "") + lines.append(f"
  • Paid on {date} - {formatted_amount}
  • ") + summary = f"
      {''.join(lines)}
    " if lines else (data.get('title', '') or "") except Exception: summary = "" rec.payment_info = summary -- cgit v1.2.3 From 6fe8ab222556bf623ee916e0d92cd5557e9b04d5 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Fri, 14 Nov 2025 14:39:40 +0700 Subject: testing commission internal --- indoteknik_custom/models/commission_internal.py | 84 ++++++++++++++++--------- 1 file changed, 56 insertions(+), 28 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/commission_internal.py b/indoteknik_custom/models/commission_internal.py index ce074bcc..bc668c28 100644 --- a/indoteknik_custom/models/commission_internal.py +++ b/indoteknik_custom/models/commission_internal.py @@ -2,6 +2,7 @@ from odoo import models, api, fields from odoo.exceptions import AccessError, UserError, ValidationError from datetime import timedelta, date import logging +import pandas as pd _logger = logging.getLogger(__name__) @@ -178,11 +179,14 @@ class CommissionInternal(models.Model): # fill later TODO @stephan def calculate_commission_internal_result(self): + exception = ['ONGKOS KIRIM SO/20'] query = [ ('commission_internal_id.id', '=', self.id), - ('helper2', '!=', False) + ('helper2', '!=', False), + ('helper1', 'not in', exception) ] lines = self.env['commission.internal.line'].search(query) + all_invoices_and_sales = [] for line in lines: list_so = list_invoice = [] list_key = line.helper2.split() @@ -202,7 +206,17 @@ class CommissionInternal(models.Model): 'salesperson': r.user_id.name, 'amount_untaxed': r.amount_untaxed, 'amount_tax': r.amount_tax, - 'amount_total': r.amount_total + 'amount_total': r.amount_total, + 'uang_masuk_line_id': line.account_move_line_id.id, + 'uang_masuk_id': line.account_move_id.id, + 'date_uang_masuk': line.date, + 'label_uang_masuk': line.label, + 'nomor_uang_masuk': line.number, + 'uang_masuk': line.balance, + # 'linenetamt_prorate': net_amount_prorate, + 'helper1': line.helper1, + 'commission_internal_id': line.commission_internal_id.id, + 'commission_internal_line_id': line.id, }) sale_data = orders.mapped(lambda r: { 'res_name': 'sale.order', @@ -213,34 +227,48 @@ class CommissionInternal(models.Model): 'salesperson': r.user_id.name, 'amount_untaxed': r.amount_untaxed, 'amount_tax': r.amount_tax, - 'amount_total': r.grand_total + 'amount_total': r.grand_total, + 'uang_masuk_line_id': line.account_move_line_id.id, + 'uang_masuk_id': line.account_move_id.id, + 'date_uang_masuk': line.date, + 'label_uang_masuk': line.label, + 'nomor_uang_masuk': line.number, + 'uang_masuk': line.balance, + # 'linenetamt_prorate': net_amount_prorate, + 'helper1': line.helper1, + 'commission_internal_id': line.commission_internal_id.id, + 'commission_internal_line_id': line.id, }) invoices_and_sales = invoice_data + sale_data - total_amount = sum(item.get('amount_total', 0.0) for item in invoices_and_sales) - for data in invoices_and_sales: - prorate = data.get('amount_total', 0.0) / total_amount - # net_amount_prorate = data.get('amount_total', 0.0) * prorate - net_amount_prorate = line.balance * prorate - self.env['commission.internal.result'].create([{ - 'commission_internal_id': line.commission_internal_id.id, - 'commission_internal_line_id': line.id, - 'date_doc': data['date'], - 'number': data['name'], - 'res_name': data['res_name'], - 'res_id': data['res_id'], - 'name': data['name'], - 'salesperson': data['salesperson'], - 'totalamt': data['amount_total'], - 'uang_masuk_line_id': line.account_move_line_id.id, - 'uang_masuk_id': line.account_move_id.id, - 'date_uang_masuk': line.date, - 'label_uang_masuk': line.label, - 'nomor_uang_masuk': line.number, - 'uang_masuk': line.balance, - 'linenetamt_prorate': net_amount_prorate, - 'helper1': line.helper1 - }]) - print(1) + sum_amount_total = sum(item['amount_total'] for item in invoices_and_sales) + for item in invoices_and_sales: + item['sum_amount_total'] = sum_amount_total + all_invoices_and_sales.extend(invoices_and_sales) + + for data in all_invoices_and_sales: + # total_amount = sum(item.get('amount_total', 0.0) for item in invoices_and_sales) + # net_amount_prorate = data.get('amount_total', 0.0) * prorate + prorate = data.get('amount_total', 0.0) / data.get('sum_amount_total', 0.0) + net_amount_prorate = data.get('uang_masuk', 0.0) * prorate + self.env['commission.internal.result'].create([{ + 'commission_internal_id': data['commission_internal_id'], + 'commission_internal_line_id': data['commission_internal_line_id'], + 'date_doc': data['date'], + 'number': data['name'], + 'res_name': data['res_name'], + 'res_id': data['res_id'], + 'name': data['name'], + 'salesperson': data['salesperson'], + 'totalamt': data['amount_total'], + 'uang_masuk_line_id': data['uang_masuk_line_id'], + 'uang_masuk_id': data['uang_masuk_id'], + 'date_uang_masuk': data['date_uang_masuk'], + 'label_uang_masuk': data['label_uang_masuk'], + 'nomor_uang_masuk': data['nomor_uang_masuk'], + 'uang_masuk': data['uang_masuk'], + 'linenetamt_prorate': net_amount_prorate, + 'helper1': data['helper1'] + }]) print(1) # this button / method works for train data in July 2025 -- cgit v1.2.3 From 5322376db0faea9de44243f2d747e68eb1713d75 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 14 Nov 2025 14:49:48 +0700 Subject: remove pandas --- indoteknik_custom/models/commission_internal.py | 1 - 1 file changed, 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/commission_internal.py b/indoteknik_custom/models/commission_internal.py index bc668c28..66d32706 100644 --- a/indoteknik_custom/models/commission_internal.py +++ b/indoteknik_custom/models/commission_internal.py @@ -2,7 +2,6 @@ from odoo import models, api, fields from odoo.exceptions import AccessError, UserError, ValidationError from datetime import timedelta, date import logging -import pandas as pd _logger = logging.getLogger(__name__) -- cgit v1.2.3 From 93f431a8d96bb46cb34ad9629d450b34a531265c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 14 Nov 2025 15:22:48 +0700 Subject: fix ccm X --- indoteknik_custom/models/tukar_guling.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 99a74505..c8375161 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -737,14 +737,12 @@ class TukarGuling(models.Model): if mapping_koli and record.operations.picking_type_id.id == 29: for prod in mapping_koli.mapped('product_id'): qty_total = sum(mk.qty_return for mk in mapping_koli.filtered(lambda m: m.product_id == prod)) - move = bu_out.move_lines.filtered(lambda m: m.product_id == prod) - if not move: - raise UserError(f"Move BU/OUT tidak ditemukan untuk produk {prod.display_name}") - srt_return_lines.append((0, 0, { - 'product_id': prod.id, - 'quantity': qty_total, - 'move_id': move.id, - })) + for ml in bu_out.move_line_ids.filtered(lambda ml: ml.qty_done > 0 and not ml.package_id): + srt_return_lines.append((0, 0, { + 'product_id': ml.product_id.id, + 'quantity': ml.qty_done, + 'move_id': ml.move_id.id, + })) _logger.info(f"📟 SRT line: {prod.display_name} | qty={qty_total}") elif not mapping_koli and record.operations.picking_type_id.id == 29: -- cgit v1.2.3 From f1bf6c4cfa31446a15e5678a80c023cadba64f86 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 14 Nov 2025 16:19:19 +0700 Subject: fix ccm --- indoteknik_custom/models/tukar_guling.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index c8375161..aa116ce3 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -737,12 +737,18 @@ class TukarGuling(models.Model): if mapping_koli and record.operations.picking_type_id.id == 29: for prod in mapping_koli.mapped('product_id'): qty_total = sum(mk.qty_return for mk in mapping_koli.filtered(lambda m: m.product_id == prod)) - for ml in bu_out.move_line_ids.filtered(lambda ml: ml.qty_done > 0 and not ml.package_id): + + move_lines = bu_out.move_line_ids.filtered( + lambda ml: ml.product_id == prod and ml.qty_done > 0 and not ml.package_id + ) + + for ml in move_lines: srt_return_lines.append((0, 0, { 'product_id': ml.product_id.id, 'quantity': ml.qty_done, 'move_id': ml.move_id.id, })) + _logger.info(f"📟 SRT line: {prod.display_name} | qty={qty_total}") elif not mapping_koli and record.operations.picking_type_id.id == 29: -- cgit v1.2.3 From 6b253ed4dc5346ee8f5cefed035ab8a2d02e90ac Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 14 Nov 2025 17:01:38 +0700 Subject: BD iu approval fix --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7c4cea8b..f69e5fa9 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1603,7 +1603,7 @@ class StockPicking(models.Model): self.tukar_guling_po_id.update_doc_state() user = self.env.user - if not user.has_group('indoteknik_custom.group_role_logistic'): + if not user.has_group('indoteknik_custom.group_role_logistic') and 'BU/IU' in self.name: raise UserWarning('Validate hnaya bisa di lakukan oleh logistik') return res -- cgit v1.2.3 From db87e7d19b369b7f0181f9ffc774cce5adc76091 Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Mon, 17 Nov 2025 10:37:04 +0700 Subject: push --- indoteknik_custom/models/refund_sale_order.py | 40 ++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index 9eae5b32..bbab3ec4 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -326,8 +326,23 @@ class RefundSaleOrder(models.Model): domain.append(('ref', 'ilike', n)) moves2 = self.env['account.move'].search(domain) + moves3 = self.env['account.move'] + if so_ids: + so_names = self.env['sale.order'].browse(so_ids).mapped('name') + domain = [ + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ('ref', 'ilike', 'uang muka penjualan') + ] + if so_names: + domain += ['|'] * (len(so_names) - 1) + for n in so_names: + domain.append(('ref', 'ilike', n)) + moves3 = self.env['account.move'].search(domain) + has_moves = bool(moves) has_moves2 = bool(moves2) + has_moves3 = bool(moves3) has_piutangmdr = bool(piutangmdr) has_piutangbca = bool(piutangbca) has_misc = bool(misc) @@ -349,6 +364,8 @@ class RefundSaleOrder(models.Model): # sisanya bisa dijumlahkan tanpa konflik if has_moves2: amounts.append(sum(moves2.mapped('amount_total_signed'))) + if has_moves3: + amounts.append(sum(moves3.mapped('amount_total_signed'))) if has_piutangbca: amounts.append(sum(piutangbca.mapped('amount_total_signed'))) if has_piutangmdr: @@ -573,9 +590,10 @@ class RefundSaleOrder(models.Model): domain = [ ('journal_id', '=', 11), ('state', '=', 'posted'), - '|', + '|', '|', ('ref', 'ilike', 'dp'), ('ref', 'ilike', 'payment'), + ('ref', 'ilike', 'uang muka penjualan'), ] domain += ['|'] * (len(so_names) - 1) for n in so_names: @@ -653,6 +671,7 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 13), ('state', '=', 'posted'), ]) + moves2 = self.env['account.move'] if so_ids: so_records = self.env['sale.order'].browse(so_ids) @@ -668,9 +687,26 @@ class RefundSaleOrder(models.Model): domain.append(('ref', 'ilike', n)) moves2 = self.env['account.move'].search(domain) + + moves3 = self.env['account.move'] + if so_ids: + so_records = self.env['sale.order'].browse(so_ids) + so_names = so_records.mapped('name') + + domain = [ + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ('ref', 'ilike', 'uang muka penjualan') + ] + domain += ['|'] * (len(so_names) - 1) + for n in so_names: + domain.append(('ref', 'ilike', n)) + + moves3 = self.env['account.move'].search(domain) has_moves = bool(moves) has_moves2 = bool(moves2) + has_moves3 = bool(moves3) has_piutangmdr = bool(piutangmdr) has_piutangbca = bool(piutangbca) has_misc = bool(misc) @@ -685,6 +721,8 @@ class RefundSaleOrder(models.Model): amounts.append(sum(moves.mapped('amount_total_signed'))) if has_moves2: amounts.append(sum(moves2.mapped('amount_total_signed'))) + if has_moves3: + amounts.append(sum(moves3.mapped('amount_total_signed'))) if has_piutangbca: amounts.append(sum(piutangbca.mapped('amount_total_signed'))) if has_piutangmdr: -- cgit v1.2.3 From bdaad593bd6b3853f98cfc8d203c49474bbb69ac Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Mon, 17 Nov 2025 10:49:48 +0700 Subject: push --- indoteknik_custom/models/refund_sale_order.py | 66 +++++++++++++-------------- 1 file changed, 33 insertions(+), 33 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index bbab3ec4..c6db2174 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -750,39 +750,39 @@ class RefundSaleOrder(models.Model): if self.sale_order_ids: self.partner_id = self.sale_order_ids[0].partner_id - @api.constrains('sale_order_ids') - def _check_sale_orders_payment(self): - """ Validasi SO harus punya uang masuk (Journal Uang Muka / Midtrans) """ - for rec in self: - invalid_orders = [] - - for so in rec.sale_order_ids: - # cari journal uang muka - moves = self.env['account.move'].search([ - ('sale_id', '=', so.id), - ('journal_id', '=', 11), # Journal Uang Muka - ('state', '=', 'posted'), - ]) - piutangbca = self.env['account.move'].search([ - ('ref', 'in', rec.invoice_ids.mapped('name')), - ('journal_id', '=', 4), - ('state', '=', 'posted'), - ]) - piutangmdr = self.env['account.move'].search([ - ('ref', 'in', rec.invoice_ids.mapped('name')), - ('journal_id', '=', 7), - ('state', '=', 'posted'), - ]) - - if not moves and so.payment_status != 'settlement' and not piutangbca and not piutangmdr: - invalid_orders.append(so.name) - - if invalid_orders: - raise ValidationError( - f"Tidak dapat membuat refund untuk SO {', '.join(invalid_orders)} " - "karena tidak memiliki Record Uang Masuk (Journal Uang Muka/Payment Invoice/Midtrans).\n" - "Pastikan semua SO yang dipilih sudah memiliki Record pembayaran yang valid." - ) + # @api.constrains('sale_order_ids') + # def _check_sale_orders_payment(self): + # """ Validasi SO harus punya uang masuk (Journal Uang Muka / Midtrans) """ + # for rec in self: + # invalid_orders = [] + + # for so in rec.sale_order_ids: + # # cari journal uang muka + # moves = self.env['account.move'].search([ + # ('sale_id', '=', so.id), + # ('journal_id', '=', 11), # Journal Uang Muka + # ('state', '=', 'posted'), + # ]) + # piutangbca = self.env['account.move'].search([ + # ('ref', 'in', rec.invoice_ids.mapped('name')), + # ('journal_id', '=', 4), + # ('state', '=', 'posted'), + # ]) + # piutangmdr = self.env['account.move'].search([ + # ('ref', 'in', rec.invoice_ids.mapped('name')), + # ('journal_id', '=', 7), + # ('state', '=', 'posted'), + # ]) + + # if not moves and so.payment_status != 'settlement' and not piutangbca and not piutangmdr: + # invalid_orders.append(so.name) + + # if invalid_orders: + # raise ValidationError( + # f"Tidak dapat membuat refund untuk SO {', '.join(invalid_orders)} " + # "karena tidak memiliki Record Uang Masuk (Journal Uang Muka/Payment Invoice/Midtrans).\n" + # "Pastikan semua SO yang dipilih sudah memiliki Record pembayaran yang valid." + # ) @api.onchange('refund_type') def _onchange_refund_type(self): -- cgit v1.2.3 From 6f254e896a0a788ac7f3a8c1668be5f31f70b640 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 17 Nov 2025 12:02:35 +0700 Subject: (andri) add field infomation line pada dunning run + fix approve due extension log --- indoteknik_custom/models/account_move_due_extension.py | 5 +++++ indoteknik_custom/models/dunning_run.py | 1 + 2 files changed, 6 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move_due_extension.py b/indoteknik_custom/models/account_move_due_extension.py index 55fc6c65..951d9745 100644 --- a/indoteknik_custom/models/account_move_due_extension.py +++ b/indoteknik_custom/models/account_move_due_extension.py @@ -115,6 +115,11 @@ class DueExtension(models.Model): self.order_id.check_credit_limit() self.order_id.approval_status = 'pengajuan1' return self.order_id._create_approval_notification('Sales Manager') + + if self.order_id._requires_approval_team_sales(): + self.order_id.check_credit_limit() + self.order_id.approval_status = 'pengajuan1' + return self.order_id._create_approval_notification('Team Sales') sales = self.env['sale.order'].browse(self.order_id.id) diff --git a/indoteknik_custom/models/dunning_run.py b/indoteknik_custom/models/dunning_run.py index 9feea1d1..2562c305 100644 --- a/indoteknik_custom/models/dunning_run.py +++ b/indoteknik_custom/models/dunning_run.py @@ -150,4 +150,5 @@ class DunningRunLine(models.Model): open_amt = fields.Float(string='Open Amount') due_date = fields.Date(string='Due Date') payment_term = fields.Many2one('account.payment.term', related='invoice_id.invoice_payment_term_id', string='Payment Term') + information_line = fields.Text(string='Information') -- cgit v1.2.3 From 0e026757427842dc865a5cbf17f2e3d85a30875c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 17 Nov 2025 17:10:38 +0700 Subject: prevent schedule action --- indoteknik_custom/models/account_move.py | 16 ++++++++++++++++ indoteknik_custom/models/purchase_order.py | 10 ++++++++++ indoteknik_custom/models/sj_tele.py | 10 ++++++++++ 3 files changed, 36 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 5e38b544..6212664e 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -11,6 +11,7 @@ import re from terbilang import Terbilang from collections import defaultdict from odoo.tools.misc import formatLang +import socket _logger = logging.getLogger(__name__) @@ -218,7 +219,16 @@ class AccountMove(models.Model): 'target': 'new', } + @staticmethod + def is_local_env(): + hostname = socket.gethostname().lower() + keywords = ['andri', 'miqdad', 'fin', 'stephan', 'hafid', 'nathan'] + return any(keyword in hostname for keyword in keywords) + def send_due_invoice_reminder(self): + if self.is_local_env(): + _logger.warning("📪 Local environment detected — skip sending email reminders.") + return today = fields.Date.today() target_dates = [ today + timedelta(days=7), @@ -240,6 +250,9 @@ class AccountMove(models.Model): self._send_invoice_reminders(invoices, mode='due') def send_overdue_invoice_reminder(self): + if self.is_local_env(): + _logger.warning("📪 Local environment detected — skip sending email reminders.") + return today = fields.Date.today() invoices = self.env['account.move'].search([ ('move_type', '=', 'out_invoice'), @@ -255,6 +268,9 @@ class AccountMove(models.Model): self._send_invoice_reminders(invoices, mode='overdue') def _send_invoice_reminders(self, invoices, mode): + if self.is_local_env(): + _logger.warning("📪 Local environment detected — skip sending email reminders.") + return today = fields.Date.today() template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 4475e777..3312e7fd 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -7,6 +7,8 @@ from pytz import timezone, utc import io import base64 from odoo.tools import lazy_property +import socket + try: from odoo.tools.misc import xlsxwriter except ImportError: @@ -121,6 +123,11 @@ class PurchaseOrder(models.Model): default=True ) + @staticmethod + def is_local_env(): + hostname = socket.gethostname().lower() + keywords = ['andri', 'miqdad', 'fin', 'stephan', 'hafid', 'nathan'] + return any(keyword in hostname for keyword in keywords) @api.onchange('show_description') def onchange_show_description(self): @@ -1140,6 +1147,9 @@ class PurchaseOrder(models.Model): break if send_email: + if self.is_local_env(): + _logger.warning("📪 Local environment detected — skip sending email reminders.") + return self._send_mail() if self.revisi_po: diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index 53ba26fc..ed363f59 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -5,6 +5,7 @@ import json import logging, subprocess import time from collections import OrderedDict +import socket _logger = logging.getLogger(__name__) @@ -20,7 +21,16 @@ class SjTele(models.Model): date_doc_kirim = fields.Datetime(string='Tanggal Kirim SJ') is_sent = fields.Boolean(default=False) + @staticmethod + def is_local_env(): + hostname = socket.gethostname().lower() + keywords = ['andri', 'miqdad', 'fin', 'stephan', 'hafid', 'nathan'] + return any(keyword in hostname for keyword in keywords) + def woi(self): + if self.is_local_env(): + _logger.warning("📪 Local environment detected — skip sending email reminders.") + return bot_mqdd = '8203414501:AAHy_XwiUAVrgRM2EJzW7sZx9npRLITZpb8' chat_id_mqdd = '-1003087280519' api_base = f'https://api.telegram.org/bot{bot_mqdd}' -- cgit v1.2.3 From 6074d548889ea0bcd489fcc6642eeaec1422cf89 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Tue, 18 Nov 2025 08:00:33 +0700 Subject: fix commission internal sales --- indoteknik_custom/models/commission_internal.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/commission_internal.py b/indoteknik_custom/models/commission_internal.py index 66d32706..cd6da380 100644 --- a/indoteknik_custom/models/commission_internal.py +++ b/indoteknik_custom/models/commission_internal.py @@ -194,8 +194,8 @@ class CommissionInternal(models.Model): list_invoice.append(key) if key[:5] == 'SO/20': list_so.append(key) - invoices = self.env['account.move'].search([('state', '=', 'posted'), ('name', 'in', list_invoice)]) - orders = self.env['sale.order'].search([('state', 'in', ['done', 'sale']), ('name', 'in', list_so)]) + invoices = self.env['account.move'].search([('name', 'in', list_invoice)]) + orders = self.env['sale.order'].search([('name', 'in', list_so)]) invoice_data = invoices.mapped(lambda r: { 'res_name': 'account.move', 'res_id': r.id, @@ -216,6 +216,7 @@ class CommissionInternal(models.Model): 'helper1': line.helper1, 'commission_internal_id': line.commission_internal_id.id, 'commission_internal_line_id': line.id, + 'helper2': r.state, }) sale_data = orders.mapped(lambda r: { 'res_name': 'sale.order', @@ -237,6 +238,7 @@ class CommissionInternal(models.Model): 'helper1': line.helper1, 'commission_internal_id': line.commission_internal_id.id, 'commission_internal_line_id': line.id, + 'helper2': r.state, }) invoices_and_sales = invoice_data + sale_data sum_amount_total = sum(item['amount_total'] for item in invoices_and_sales) @@ -266,7 +268,8 @@ class CommissionInternal(models.Model): 'nomor_uang_masuk': data['nomor_uang_masuk'], 'uang_masuk': data['uang_masuk'], 'linenetamt_prorate': net_amount_prorate, - 'helper1': data['helper1'] + 'helper1': data['helper1'], + 'helper2': data['helper2'] }]) print(1) -- cgit v1.2.3 From 696a0ef4e25abd39a013503694be11ca2b0645ca Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 18 Nov 2025 10:14:25 +0700 Subject: (andri) add id transit CNY & HO --- indoteknik_custom/models/advance_payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index 42097923..d96fc8ee 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -1421,7 +1421,7 @@ class AdvancePaymentCreateBill(models.TransientModel): apr_id = fields.Many2one('advance.payment.request', string='Advance Payment Request', required=True) account_id = fields.Many2one( 'account.account', string='Bank Intransit', required=True, - domain="[('id', 'in', [573, 389, 392])]" # ID Bank Intransit + domain="[('id', 'in', [573, 389, 392, 683, 380])]" # ID Bank Intransit ) nominal = fields.Float(string='Nominal', related='apr_id.nominal') -- cgit v1.2.3 From ecdcf707992e0816a22619e7b87156eab8c9ed0c Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 18 Nov 2025 10:49:19 +0700 Subject: (andri) fix bug confirm payment PUM --- indoteknik_custom/models/advance_payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index d96fc8ee..ec23de63 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -505,7 +505,7 @@ class AdvancePaymentRequest(models.Model): for rec in self: if not rec.attachment_file_image and not rec.attachment_file_pdf: raise UserError( - f'Tidak bisa konfirmasi pembayaran PUM {rec.name or ""} ' + f'Tidak bisa konfirmasi pembayaran {rec.number or ""} ' f'karena belum ada bukti attachment (PDF/Image).' ) -- cgit v1.2.3 From 61a1d7e817de31b52285b6bf6448c321f1beac08 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 18 Nov 2025 11:34:19 +0700 Subject: tracking & readonly sales team --- indoteknik_custom/models/sale_order.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 14b66d87..2ed4046f 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -398,7 +398,10 @@ class SaleOrder(models.Model): compute="_compute_partner_is_cbd_locked" ) internal_notes_contact = fields.Text(related='partner_id.comment', string="Internal Notes", readonly=True, help="Internal Notes dari contact utama customer.") - is_so_fiktif = fields.Boolean('SO Fiktif?', tracking=3) + is_so_fiktif = fields.Boolean('SO Fiktif?', tracking=3) + team_id = fields.Many2one(tracking=True) + + def action_set_shipping_id(self): for rec in self: -- cgit v1.2.3 From b3e037e6d9f67e63803f6e56929f7c10b61074a3 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 18 Nov 2025 15:36:05 +0700 Subject: (andri) fix linked STJ PO --- indoteknik_custom/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f69e5fa9..e7686b75 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1541,9 +1541,9 @@ class StockPicking(models.Model): self.check_koli() res = super(StockPicking, self).button_validate() - # Penambahan link PO di Stock Journal untuk Picking BD + # Penambahan link PO di Stock Journal for picking in self: - if picking.name and 'BD/' in picking.name and picking.purchase_id: + if picking.name and picking.purchase_id: stock_journal = self.env['account.move'].search([ ('ref', 'ilike', picking.name + '%'), ('journal_id', '=', 3) # Stock Journal ID -- cgit v1.2.3 From 27aa909290c87086493d060caa98ba396c257ab7 Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Wed, 19 Nov 2025 13:16:20 +0700 Subject: reklas nyangkut --- indoteknik_custom/models/refund_sale_order.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index c6db2174..cbc0b717 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -332,7 +332,8 @@ class RefundSaleOrder(models.Model): domain = [ ('journal_id', '=', 11), ('state', '=', 'posted'), - ('ref', 'ilike', 'uang muka penjualan') + ('ref', 'ilike', 'uang muka penjualan'), + ('ref', 'not ilike', 'reklas'), ] if so_names: domain += ['|'] * (len(so_names) - 1) @@ -696,7 +697,8 @@ class RefundSaleOrder(models.Model): domain = [ ('journal_id', '=', 11), ('state', '=', 'posted'), - ('ref', 'ilike', 'uang muka penjualan') + ('ref', 'ilike', 'uang muka penjualan'), + ('ref', 'not ilike', 'reklas'), ] domain += ['|'] * (len(so_names) - 1) for n in so_names: -- cgit v1.2.3 From 029ecb4f6f82cadd930991a0e798cc2b19ad9e86 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 19 Nov 2025 14:21:30 +0700 Subject: (andri) fix log --- indoteknik_custom/models/account_move_due_extension.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move_due_extension.py b/indoteknik_custom/models/account_move_due_extension.py index 951d9745..352200e0 100644 --- a/indoteknik_custom/models/account_move_due_extension.py +++ b/indoteknik_custom/models/account_move_due_extension.py @@ -127,6 +127,9 @@ class DueExtension(models.Model): self.order_id.due_id = self.id self.approve_by = self.env.user.id self.date_approve = datetime.utcnow() + + # self.order_id.message_post("Due Extension telah di approve") + self.order_id.message_post(f"Due Extension {self.number} telah di approve") template = self.env.ref('indoteknik_custom.mail_template_due_extension_approve') template.send_mail(self.id, force_send=True) return { -- cgit v1.2.3