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 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/__manifest__.py | 1 + indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/down_payment.py | 49 ++++++++++++--- indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/down_payment.xml | 87 ++++++++++++++++++++++++++ indoteknik_custom/views/ir_sequence.xml | 10 +++ 6 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 indoteknik_custom/views/down_payment.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 21afc26f..7539f23f 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -170,6 +170,7 @@ 'views/public_holiday.xml', 'views/stock_inventory.xml', 'views/sale_order_delay.xml', + 'views/down_payment.xml', ], 'demo': [], 'css': [], 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): + diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 2b970cfd..c4347709 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -183,3 +183,4 @@ access_production_purchase_match,access.production.purchase.match,model_producti access_image_carousel,access.image.carousel,model_image_carousel,,1,1,1,1 access_v_sale_notin_matchpo,access.v.sale.notin.matchpo,model_v_sale_notin_matchpo,,1,1,1,1 access_approval_payment_term,access.approval.payment.term,model_approval_payment_term,,1,1,1,1 +access_down_payment,access.down.payment,model_down_payment,,1,1,1,1 \ No newline at end of file diff --git a/indoteknik_custom/views/down_payment.xml b/indoteknik_custom/views/down_payment.xml new file mode 100644 index 00000000..f418b932 --- /dev/null +++ b/indoteknik_custom/views/down_payment.xml @@ -0,0 +1,87 @@ + + + + down.payment.form + down.payment + +
+
+
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + down.payment.tree + down.payment + + + + + + + + + + + + + + Pengajuan Uang Muka (Down Payment) + ir.actions.act_window + down.payment + tree,form + + + +
\ No newline at end of file diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index f2b42c3b..e959f562 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -200,5 +200,15 @@ 1 1 + + + Down Payment Sequence + down.payment + PUM/%(year)s/ + 5 + 1 + 1 + True + \ No newline at end of file -- 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/__manifest__.py | 1 + indoteknik_custom/models/down_payment.py | 138 ++++++++++++++++----- indoteknik_custom/security/ir.model.access.csv | 5 +- indoteknik_custom/views/down_payment.xml | 45 +++++-- .../views/down_payment_realization.xml | 69 +++++++++++ 5 files changed, 215 insertions(+), 43 deletions(-) create mode 100644 indoteknik_custom/views/down_payment_realization.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 7539f23f..29039f8b 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -171,6 +171,7 @@ 'views/stock_inventory.xml', 'views/sale_order_delay.xml', 'views/down_payment.xml', + 'views/down_payment_realization.xml', ], 'demo': [], 'css': [], 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 + diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index c4347709..349ed40b 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -183,4 +183,7 @@ access_production_purchase_match,access.production.purchase.match,model_producti access_image_carousel,access.image.carousel,model_image_carousel,,1,1,1,1 access_v_sale_notin_matchpo,access.v.sale.notin.matchpo,model_v_sale_notin_matchpo,,1,1,1,1 access_approval_payment_term,access.approval.payment.term,model_approval_payment_term,,1,1,1,1 -access_down_payment,access.down.payment,model_down_payment,,1,1,1,1 \ No newline at end of file +access_down_payment,access.down.payment,model_down_payment,,1,1,1,1 +access_realization_down_payment,access.realization.down.payment,model_realization_down_payment,,1,1,1,1 +access_realization_down_payment_line,access.realization.down.payment.line,model_realization_down_payment_line,,1,1,1,1 +access_realization_down_payment_use_line,access.realization.down.payment.use.line,model_realization_down_payment_use_line,,1,1,1,1 \ No newline at end of file diff --git a/indoteknik_custom/views/down_payment.xml b/indoteknik_custom/views/down_payment.xml index f418b932..3830154f 100644 --- a/indoteknik_custom/views/down_payment.xml +++ b/indoteknik_custom/views/down_payment.xml @@ -11,10 +11,14 @@ string="Realisasi" class="btn-primary" attrs="{}"/> + + +
+

+ +

+
@@ -43,8 +63,10 @@ +
+ + + + + down.payment.ap.only.form + down.payment.ap.only + +
+ + + + +
+
+
+
+
+ + + Create CAB AP Only + down.payment.ap.only + form + + new + + \ No newline at end of file -- 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 +++++++++++++++++----- .../views/down_payment_realization.xml | 4 ++ 2 files changed, 63 insertions(+), 14 deletions(-) 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() diff --git a/indoteknik_custom/views/down_payment_realization.xml b/indoteknik_custom/views/down_payment_realization.xml index 2ee1a6d5..1138b59e 100644 --- a/indoteknik_custom/views/down_payment_realization.xml +++ b/indoteknik_custom/views/down_payment_realization.xml @@ -9,6 +9,9 @@ type="object" string="Validasi" class="btn-primary"/> + + +
+

+ +

+
@@ -29,6 +45,10 @@ + -- 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 ++++++++++++++++++++- .../views/down_payment_realization.xml | 4 + 2 files changed, 138 insertions(+), 4 deletions(-) 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' diff --git a/indoteknik_custom/views/down_payment_realization.xml b/indoteknik_custom/views/down_payment_realization.xml index 64b3af91..e20a5e66 100644 --- a/indoteknik_custom/views/down_payment_realization.xml +++ b/indoteknik_custom/views/down_payment_realization.xml @@ -9,6 +9,10 @@ type="object" string="Validasi" class="btn-primary"/> + + +
+

+ +

+
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+
+ +
+ +
+ + +
+ +
+ + + + advance.payment.request.tree + advance.payment.request + + + + + + + + + + + + + + + + + Advance Payment Request + ir.actions.act_window + advance.payment.request + tree,form + + + + + + + + advance.payment.create.bill.form + advance.payment.create.bill + +
+ + + + +
+
+
+
+
+ + + Create CAB AP Only + advance.payment.create.bill + form + + new + + + + advance.payment.request.reject.form + advance.payment.request.reject + +
+ + + +
+
+
+
+
+ + + Tolak Pengajuan APR + advance.payment.request.reject + form + new + + + \ No newline at end of file diff --git a/indoteknik_custom/views/advance_payment_settlement.xml b/indoteknik_custom/views/advance_payment_settlement.xml new file mode 100644 index 00000000..a2bb31b2 --- /dev/null +++ b/indoteknik_custom/views/advance_payment_settlement.xml @@ -0,0 +1,168 @@ + + + advance.payment.settlement.form + advance.payment.settlement + +
+
+
+ +
+ + +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + +
+ +
+ + +
+ +
+
+ + + Advance Payment Settlement + advance.payment.settlement + tree,form + +
\ No newline at end of file -- cgit v1.2.3 From c6aab4fe00522fee889a4c505ed2415a62ae7161 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 2 Oct 2025 15:32:17 +0700 Subject: (andri) fix --- indoteknik_custom/__manifest__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 94e67c1c..0c05a290 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -99,7 +99,6 @@ 'views/mail_template_efaktur.xml', 'views/mail_template_invoice_po.xml', 'views/mail_template_invoice_reminder.xml', - 'views/mail_template_pum.xml', 'views/price_group.xml', 'views/mrp_production.xml', 'views/apache_solr.xml', -- 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 -------------------- indoteknik_custom/views/down_payment.xml | 208 ---- .../views/down_payment_realization.xml | 168 --- 3 files changed, 1464 deletions(-) delete mode 100644 indoteknik_custom/models/down_payment.py delete mode 100644 indoteknik_custom/views/down_payment.xml delete mode 100644 indoteknik_custom/views/down_payment_realization.xml 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', - } - diff --git a/indoteknik_custom/views/down_payment.xml b/indoteknik_custom/views/down_payment.xml deleted file mode 100644 index 020ed110..00000000 --- a/indoteknik_custom/views/down_payment.xml +++ /dev/null @@ -1,208 +0,0 @@ - - - - down.payment.form - down.payment - -
-
-
- -
- - -
-
-

- -

-
- - - - - - - - - -
- - - - - - - - - - -
- - - - - - - -
-
- -
-
-
- - -
-
-
-
- - down.payment.tree - down.payment - - - - - - - - - - - - - - - - Pengajuan Uang Muka (Down Payment) - ir.actions.act_window - down.payment - tree,form - - - - - - - - - down.payment.ap.only.form - down.payment.ap.only - -
- - - - -
-
-
-
-
- - - Create CAB AP Only - down.payment.ap.only - form - - new - - - - - reject.reason.downpayment.form - reject.reason.downpayment - -
- - - -
-
-
-
-
- - - Tolak Pengajuan PUM - reject.reason.downpayment - form - new - - -
\ No newline at end of file diff --git a/indoteknik_custom/views/down_payment_realization.xml b/indoteknik_custom/views/down_payment_realization.xml deleted file mode 100644 index 292d8683..00000000 --- a/indoteknik_custom/views/down_payment_realization.xml +++ /dev/null @@ -1,168 +0,0 @@ - - - realization.down.payment.form - realization.down.payment - -
-
-
- -
- - -
-
-

- -

-
- - - - - - - - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - - - - - -
- -
- - -
- -
-
- - - Realisasi PUM - realization.down.payment - tree,form - -
-- cgit v1.2.3 From 176fc6b9ffa917f1b8bd1849e525da511179805c Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 2 Oct 2025 15:47:02 +0700 Subject: (andri) fix --- indoteknik_custom/views/mail_template_pum.xml | 30 ++++++++++++--------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/indoteknik_custom/views/mail_template_pum.xml b/indoteknik_custom/views/mail_template_pum.xml index 2863741e..fba71e14 100644 --- a/indoteknik_custom/views/mail_template_pum.xml +++ b/indoteknik_custom/views/mail_template_pum.xml @@ -2,23 +2,21 @@ - - Reminder PUM: Hari Ini - - Reminder Realisasi PUM - ${object.number} + Reminder Settlement APR: Hari Ini + + Reminder Settlement APR - ${object.number} ${object.email_ap} andrifebriyadiputra@gmail.com -

Dengan Hormat Bpk/Ibu ${object.user_id.display_name},

- Berikut terlampir pengajuan PUM ${object.number} sebesar + Berikut terlampir pengajuan APR ${object.number} sebesar ${format_amount(object.nominal, object.currency_id)} dari PT. INDOTEKNIK DOTCOM GEMILANG pada tanggal ${format_date(object.create_date, 'd MMMM yyyy')}.
- Tolong segera realisasikan PUM tersebut. dengan menyertakan dokumen asli untuk mendukung realisasi PUM tersebut + Tolong segera selesaikan settlement (realisasi) APR tersebut dengan menyertakan dokumen asli untuk mendukung settlement tersebut maksimal 7 hari dari sekarang.
Terima Kasih

@@ -33,29 +31,27 @@ PT. INDOTEKNIK DOTCOM GEMILANG
Indoteknik

-

Email ini dikirim otomatis, abaikan bila sudah melakukan realisasi.

+

Email ini dikirim otomatis, abaikan bila sudah melakukan settlement/realisasi.

- - Reminder PUM: H-2 - - Reminder Realisasi PUM (H-2) - ${object.number} + Reminder Settlement APR: H-2 + + Reminder Settlement APR (H-2) - ${object.number} ${object.email_ap} andrifebriyadiputra@gmail.com -

Dengan Hormat Bpk/Ibu ${object.user_id.display_name},

- Berikut terlampir pengajuan PUM ${object.number} sebesar + Berikut terlampir pengajuan APR ${object.number} sebesar ${format_amount(object.nominal, object.currency_id)} dari PT. INDOTEKNIK DOTCOM GEMILANG pada tanggal ${format_date(object.create_date, 'd MMMM yyyy')}.
- Tolong segera realisasikan PUM tersebut. dengan menyertakan dokumen asli untuk mendukung realisasi PUM tersebut + Tolong segera selesaikan settlement (realisasi) APR tersebut dengan menyertakan dokumen asli untuk mendukung settlement tersebut batas waktu tersisa 2 hari lagi.
Terima Kasih

@@ -70,11 +66,11 @@ PT. INDOTEKNIK DOTCOM GEMILANG
Indoteknik

-

Email ini dikirim otomatis, abaikan bila sudah melakukan realisasi.

+

Email ini dikirim otomatis, abaikan bila sudah melakukan settlement/realisasi.

-
+ \ No newline at end of file -- cgit v1.2.3 From 3a6a1723928fb250f08376107be006b0567a3f1a Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 3 Oct 2025 09:54:10 +0700 Subject: (andri) fix seq --- indoteknik_custom/views/ir_sequence.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index 9ab4dd22..8fdfcbca 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -219,9 +219,9 @@ 1 - - Down Payment Sequence - down.payment + + Advance Payment Request Sequence + advance.payment.request PUM/%(year)s/%(month)s/ 4 1 -- 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 --- .../models/advance_payment_request.py | 4 +++ .../views/advance_payment_request.xml | 36 +++++++++++++--------- 2 files changed, 25 insertions(+), 15 deletions(-) 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: diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml index 1610293f..20f43961 100644 --- a/indoteknik_custom/views/advance_payment_request.xml +++ b/indoteknik_custom/views/advance_payment_request.xml @@ -62,15 +62,18 @@ + -
+
+ + - + - - - - - - -
-
+ + + + + +
+
+ +

Hello World HEHE

+
+
-- 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(+) 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 ++++++++++++++++-- .../views/advance_payment_request.xml | 34 ++++++++++++---------- 2 files changed, 44 insertions(+), 18 deletions(-) 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() diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml index 20f43961..0c851d78 100644 --- a/indoteknik_custom/views/advance_payment_request.xml +++ b/indoteknik_custom/views/advance_payment_request.xml @@ -29,18 +29,20 @@ type="object" string="Konfirmasi Pembayaran" class="btn-info" - attrs="{'invisible': ['|', ('status', '=', 'reject'), ('status_pay_down_payment', '=', 'payment')]}"/> + attrs="{'invisible': ['|', ('status', 'in', ['reject','draft']), ('status_pay_down_payment', '=', 'payment')]}"/>
- + @@ -76,9 +76,9 @@ - - - + + + @@ -89,7 +89,7 @@ widget="badge" invisible = "1"/> - + Date: Fri, 10 Oct 2025 09:30:55 +0700 Subject: (andri) penyesuaian jabatan dan department + fix view --- .../models/advance_payment_request.py | 61 ++++++++++++++-------- .../views/advance_payment_request.xml | 15 +++--- 2 files changed, 48 insertions(+), 28 deletions(-) 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') diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml index 0ec5900a..134ec9bf 100644 --- a/indoteknik_custom/views/advance_payment_request.xml +++ b/indoteknik_custom/views/advance_payment_request.xml @@ -29,12 +29,12 @@ type="object" string="Konfirmasi Pembayaran" class="btn-info" - attrs="{'invisible': ['|', ('status', 'in', ['reject','draft']), ('status_pay_down_payment', '=', 'payment')]}"/> + attrs="{'invisible': ['|', ('status', 'not in', ['approved']), ('status_pay_down_payment', '=', 'payment')]}"/> + + + +
-- 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 +++++--- indoteknik_custom/views/advance_payment_settlement.xml | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) 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 ) diff --git a/indoteknik_custom/views/advance_payment_settlement.xml b/indoteknik_custom/views/advance_payment_settlement.xml index d6eaa649..8c831be4 100644 --- a/indoteknik_custom/views/advance_payment_settlement.xml +++ b/indoteknik_custom/views/advance_payment_settlement.xml @@ -125,8 +125,9 @@
- - + + +
-- cgit v1.2.3 From e19f7637f648fb37fc9049ce7898dfc22568bf63 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 09:42:13 +0700 Subject: fix driver arrival date api for indoteknik delivery --- indoteknik_api/controllers/api_v1/stock_picking.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 2ec1ec2a..9affb492 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -1,5 +1,7 @@ +from adodbapi.apibase import DateTime + from .. import controller -from odoo import http +from odoo import http, fields from odoo.http import request, Response from pytz import timezone from datetime import datetime @@ -140,19 +142,19 @@ class StockPicking(controller.Controller): if not picking_data: return self.response(code=403, description='picking not found') - params = { - 'driver_arrival_date': datetime.utcnow(), - } + params = {} if sj_document: params['sj_documentation'] = sj_document if paket_document: params['paket_documentation'] = paket_document + params['driver_arrival_date'] = datetime.utcnow() if dispatch_document: params['dispatch_documentation'] = dispatch_document picking_data.write(params) return self.response({'name': picking_data.name}) + @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def update_status_from_biteship(self, **kw): _logger.info("Biteship Webhook: Request received at controller start (type='json').") -- 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(-) 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(+) 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(-) 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 6e746afb7fcd7a3762dc7beb27ac3f77a21c7a98 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 13:51:30 +0700 Subject: fix --- indoteknik_api/controllers/api_v1/stock_picking.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 9affb492..310554c2 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -1,5 +1,3 @@ -from adodbapi.apibase import DateTime - from .. import controller from odoo import http, fields from odoo.http import request, Response -- cgit v1.2.3 From e65db92c25f8e54773cd1113736005f20799ea2b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 13:51:45 +0700 Subject: fix --- indoteknik_api/controllers/api_v1/stock_picking.py | 1 - 1 file changed, 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 310554c2..20a48886 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -8,7 +8,6 @@ import logging _logger = logging.getLogger(__name__) -_logger = logging.getLogger(__name__) class StockPicking(controller.Controller): prefix = '/api/v1/' -- cgit v1.2.3 From 7c7519d2031a474901702fdd1c7921389eb44a15 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 16:10:38 +0700 Subject: get self pickup params from website --- indoteknik_api/controllers/api_v1/stock_picking.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 20a48886..b7ff5690 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -127,6 +127,7 @@ class StockPicking(controller.Controller): sj_document = kw.get('sj_document') if 'sj_document' in kw else None paket_document = kw.get('paket_document') if 'paket_document' in kw else None dispatch_document = kw.get('dispatch_document') if 'dispatch_document' in kw else None + self_pu= kw.get('self_pu') if 'self_pu' in kw else None # ===== Cari picking by id / picking_code ===== picking_data = False @@ -142,6 +143,8 @@ class StockPicking(controller.Controller): params = {} if sj_document: params['sj_documentation'] = sj_document + if params['self_pu']: + params['driver_arrival_date'] = datetime.utcnow() if paket_document: params['paket_documentation'] = paket_document params['driver_arrival_date'] = datetime.utcnow() -- cgit v1.2.3 From 8d649f97dade329859b5770d1f3972cdd7233f97 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 16:22:17 +0700 Subject: get self pickup params from website --- indoteknik_api/controllers/api_v1/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index b7ff5690..fe82e665 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -143,7 +143,7 @@ class StockPicking(controller.Controller): params = {} if sj_document: params['sj_documentation'] = sj_document - if params['self_pu']: + if self_pu: params['driver_arrival_date'] = datetime.utcnow() if paket_document: params['paket_documentation'] = paket_document -- 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 ++++++++++++--------- .../views/advance_payment_settlement.xml | 21 +- 2 files changed, 184 insertions(+), 156 deletions(-) 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 diff --git a/indoteknik_custom/views/advance_payment_settlement.xml b/indoteknik_custom/views/advance_payment_settlement.xml index 8c831be4..72af17d8 100644 --- a/indoteknik_custom/views/advance_payment_settlement.xml +++ b/indoteknik_custom/views/advance_payment_settlement.xml @@ -5,11 +5,6 @@
-
- - -
@@ -36,7 +28,7 @@ name="action_view_journal_uangmuka" class="oe_stat_button" icon="fa-book" - attrs="{'invisible': [('is_cab_visible', '=', False)], 'readonly': [('is_current_user_ap', '=', False)]}" + attrs="{'invisible': [('is_cab_visible', '=', False)], 'readonly': [('is_current_user_ap', '=', False)]}" style="width: 200px;"> @@ -58,10 +50,6 @@ - @@ -145,10 +133,8 @@ - - @@ -178,10 +164,7 @@ - - + -- 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(-) 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(-) 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 +++--- indoteknik_custom/views/advance_payment_request.xml | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) 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: diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml index 39f8f588..6a0d61b1 100644 --- a/indoteknik_custom/views/advance_payment_request.xml +++ b/indoteknik_custom/views/advance_payment_request.xml @@ -25,12 +25,22 @@ type="object" string="Buat Jurnal PUM" class="btn-info" - attrs="{'invisible': ['|', ('status', 'not in', ['approved']), ('is_cab_visible', '=', True)]}"/> + attrs="{'invisible': ['|', + ('status', 'not in', ['approved']), + '|', + ('is_cab_visible', '=', True), + ('type_request', '!=', 'pum') + ]}"/>
- Reminder Settlement APR: H-2 + Reminder Realisasi PUM: H-2 - Reminder Settlement APR (H-2) - ${object.number} + Reminder Realisasi PUM (H-2) - ${object.number} ${object.email_ap} andrifebriyadiputra@gmail.com @@ -48,10 +48,10 @@

Dengan Hormat Bpk/Ibu ${object.user_id.display_name},

- Berikut terlampir pengajuan APR ${object.number} sebesar + Berikut terlampir pengajuan PUM ${object.number} sebesar ${format_amount(object.nominal, object.currency_id)} dari PT. INDOTEKNIK DOTCOM GEMILANG pada tanggal ${format_date(object.create_date, 'd MMMM yyyy')}.
- Tolong segera selesaikan settlement (realisasi) APR tersebut dengan menyertakan dokumen asli untuk mendukung settlement tersebut + Tolong segera selesaikan realisasi PUM tersebut dengan menyertakan dokumen asli untuk mendukung PUM tersebut batas waktu tersisa 2 hari lagi.
Terima Kasih

@@ -66,7 +66,7 @@ PT. INDOTEKNIK DOTCOM GEMILANG
Indoteknik

-

Email ini dikirim otomatis, abaikan bila sudah melakukan settlement/realisasi.

+

Email ini dikirim otomatis, abaikan bila sudah melakukan realisasi.

-- 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(-) 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(-) 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 + indoteknik_custom/views/account_move.xml | 1 + indoteknik_custom/views/sale_order.xml | 1 + 4 files changed, 4 insertions(+) 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") diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml index ba86277a..fd59f9f6 100644 --- a/indoteknik_custom/views/account_move.xml +++ b/indoteknik_custom/views/account_move.xml @@ -80,6 +80,7 @@ type="object" class="btn-primary" help="Sync Janji Bayar Customer ke Invoices dengan jumlah Due Date yang sama"/> + diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 82daa36f..eb40b0c3 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -152,6 +152,7 @@ + -- 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(-) 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(-) 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 c07c5dbac7a12b8c9a711cdcbde748dfe85295c5 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 28 Oct 2025 11:50:28 +0700 Subject: (andri) move posisi --- indoteknik_custom/views/account_move.xml | 3 ++- indoteknik_custom/views/sale_order.xml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml index fd59f9f6..9df03674 100644 --- a/indoteknik_custom/views/account_move.xml +++ b/indoteknik_custom/views/account_move.xml @@ -63,6 +63,7 @@ decoration-info="payment_difficulty == 'normal'" decoration-warning="payment_difficulty in ('agak_sulit', 'sulit')" decoration-danger="payment_difficulty == 'bermasalah'"/> + @@ -80,7 +81,7 @@ type="object" class="btn-primary" help="Sync Janji Bayar Customer ke Invoices dengan jumlah Due Date yang sama"/> - + diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index eb40b0c3..a540caa7 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -135,6 +135,7 @@ 1 + @@ -152,7 +153,6 @@ - -- 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(-) 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 ++++++++++++++-------- .../views/advance_payment_request.xml | 3 +- 2 files changed, 45 insertions(+), 24 deletions(-) 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) diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml index c33a2843..2a8e1318 100644 --- a/indoteknik_custom/views/advance_payment_request.xml +++ b/indoteknik_custom/views/advance_payment_request.xml @@ -88,7 +88,8 @@ - + +

*Nominal terisi otomatis sesuai grand total rincian reimburse

-- 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(-) 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(-) 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(-) 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 ++++++++++++++++++++++++++++ indoteknik_custom/views/stock_picking.xml | 6 ++ 5 files changed, 152 insertions(+), 1 deletion(-) 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): diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 44ab6355..050fc819 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -226,6 +226,10 @@ + + + + @@ -394,6 +398,8 @@ decoration-danger="qty_done>product_uom_qty and state!='done' and parent.picking_type_code != 'incoming'" decoration-success="qty_done==product_uom_qty and state!='done' and not result_package_id"> + +
-- 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(-) 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(-) 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(-) 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(-) 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(-) 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 ++++++++-- indoteknik_custom/views/advance_payment_settlement.xml | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) 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') diff --git a/indoteknik_custom/views/advance_payment_settlement.xml b/indoteknik_custom/views/advance_payment_settlement.xml index 1a9d7908..188f09d0 100644 --- a/indoteknik_custom/views/advance_payment_settlement.xml +++ b/indoteknik_custom/views/advance_payment_settlement.xml @@ -47,7 +47,9 @@ - + +

*Lengkapi keterangan berikut selaras dengan realisasi yang dilakukan (sesuai dengan format surat yang ada)

+

-- cgit v1.2.3 From 2a57a75b636128cda3d1ee321a926643c461dacf Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 30 Oct 2025 17:51:10 +0700 Subject: (andri) fix --- indoteknik_custom/views/advance_payment_request.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml index 2a8e1318..f8217150 100644 --- a/indoteknik_custom/views/advance_payment_request.xml +++ b/indoteknik_custom/views/advance_payment_request.xml @@ -85,7 +85,7 @@ - + -- 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 ++-- indoteknik_custom/views/stock_picking.xml | 6 +- 4 files changed, 93 insertions(+), 47 deletions(-) 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 diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 050fc819..288a91c9 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -398,8 +398,10 @@ decoration-danger="qty_done>product_uom_qty and state!='done' and parent.picking_type_code != 'incoming'" decoration-success="qty_done==product_uom_qty and state!='done' and not result_package_id"> - - + + + + -- 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 ++++++++++-------------------- indoteknik_custom/views/stock_picking.xml | 8 +- 2 files changed, 41 insertions(+), 84 deletions(-) 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 diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 288a91c9..78594375 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -398,10 +398,10 @@ decoration-danger="qty_done>product_uom_qty and state!='done' and parent.picking_type_code != 'incoming'" decoration-success="qty_done==product_uom_qty and state!='done' and not result_package_id"> - - - - + + + + -- 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(-) 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(-) 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 58fdd12bb5ea91cbe65f23472879dd690d5efbbf Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Fri, 31 Oct 2025 15:52:49 +0700 Subject: push --- indoteknik_custom/views/refund_sale_order.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/views/refund_sale_order.xml b/indoteknik_custom/views/refund_sale_order.xml index afa7c1cb..fbe17093 100644 --- a/indoteknik_custom/views/refund_sale_order.xml +++ b/indoteknik_custom/views/refund_sale_order.xml @@ -57,7 +57,7 @@