From 74953e4b98abca66898426586907a6ed22ef4139 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 28 Jul 2025 15:20:43 +0700 Subject: (andri) add flag reminder invoices di contact company --- indoteknik_custom/models/res_partner.py | 5 +++++ indoteknik_custom/views/res_partner.xml | 3 +++ 2 files changed, 8 insertions(+) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 236df16f..d1d4ca02 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -27,6 +27,11 @@ class ResPartner(models.Model): # Referensi supplier_ids = fields.Many2many('user.pengajuan.tempo.line', string="Suppliers") + reminder_invoices = fields.Boolean( + string='Reminder Invoice', + help='Centang jika kontak ini harus menerima email pengingat invoice.', default=False + ) + # informasi perusahaan name_tempo = fields.Many2one('res.partner', string='Nama Perusahaan',tracking=True) industry_id_tempo = fields.Many2one('res.partner.industry', 'Customer Industry', readonly=True) diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index a030a75c..08eca7ea 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -102,6 +102,9 @@ /> + + + 0 -- cgit v1.2.3 From 8628973dfbfc76f3f7fea5a01d5ccad4998f7f3f Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 28 Jul 2025 15:37:22 +0700 Subject: (andri) fix send due invoices reminder --- indoteknik_custom/models/account_move.py | 203 ++++++++++++++++--------------- indoteknik_custom/models/res_partner.py | 2 +- 2 files changed, 108 insertions(+), 97 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 1a6fad1c..14927bec 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -109,102 +109,113 @@ class AccountMove(models.Model): # result.append((move.id, move.display_name)) # return result - # def send_due_invoice_reminder(self): - # today = fields.Date.today() - # target_dates = [ - # today - timedelta(days=7), - # today - timedelta(days=3), - # today, - # today + timedelta(days=3), - # today + timedelta(days=7), - # ] - - # partner = self.env['res.partner'].search([('name', 'ilike', 'BANGUNAN TEKNIK GRUP')], limit=1) - # if not partner: - # _logger.info("Partner tidak ditemukan.") - # return - - # invoices = self.env['account.move'].search([ - # ('move_type', '=', 'out_invoice'), - # ('state', '=', 'posted'), - # ('payment_state', 'not in', ['paid','in_payment', 'reversed']), - # ('invoice_date_due', 'in', target_dates), - # ('partner_id', '=', partner.id), - # ]) - - # _logger.info(f"Invoices tahap 1: {invoices}") - - # invoices = invoices.filtered( - # lambda inv: inv.invoice_payment_term_id and 'tempo' in (inv.invoice_payment_term_id.name or '').lower() - # ) - # _logger.info(f"Invoices tahap 2: {invoices}") - - # if not invoices: - # _logger.info(f"Tidak ada invoice yang due untuk partner: {partner.name}") - # return - - # grouped = {} - # for inv in invoices: - # grouped.setdefault(inv.partner_id, []).append(inv) - - # template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') - - # for partner, invs in grouped.items(): - # if not partner.email: - # _logger.info(f"Partner {partner.name} tidak memiliki email") - # continue - - # invoice_table_rows = "" - # for inv in invs: - # days_to_due = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 - # invoice_table_rows += f""" - # - # {inv.name} - # {fields.Date.to_string(inv.invoice_date) or '-'} - # {fields.Date.to_string(inv.invoice_date_due) or '-'} - # {days_to_due} - # {formatLang(self.env, inv.amount_total, currency_obj=inv.currency_id)} - # {inv.ref or '-'} - # - # """ - - # subject = f"Reminder Invoice Due - {partner.name}" - # body_html = re.sub( - # r"]*>.*?", - # f"{invoice_table_rows}", - # template.body_html, - # flags=re.DOTALL - # ).replace('${object.name}', partner.name) \ - # .replace('${object.partner_id.name}', partner.name) - # # .replace('${object.email}', partner.email or '') - - # values = { - # 'subject': subject, - # 'email_to': 'andrifebriyadiputra@gmail.com', # Ubah ke partner.email untuk produksi - # 'email_from': 'finance@indoteknik.co.id', - # 'body_html': body_html, - # 'reply_to': f'invoice+account.move_{invs[0].id}@indoteknik.co.id', - # } - - # _logger.info(f"VALUES: {values}") - - # template.send_mail(invs[0].id, force_send=True, email_values=values) - - # # Default System User - # user_system = self.env['res.users'].browse(25) - # system_id = user_system.partner_id.id if user_system else False - # _logger.info(f"System User: {user_system.name} ({user_system.id})") - # _logger.info(f"System User ID: {system_id}") - - # for inv in invs: - # inv.message_post( - # subject=subject, - # body=body_html, - # subtype_id=self.env.ref('mail.mt_note').id, - # author_id=system_id, - # ) - - # _logger.info(f"Reminder terkirim ke {partner.name} ({values['email_to']}) → {len(invs)} invoice") + def send_due_invoice_reminder(self): + today = fields.Date.today() + target_dates = [ + today - timedelta(days=7), + today - timedelta(days=3), + today, + today + timedelta(days=3), + today + timedelta(days=7), + ] + + # Contoh khusus partner tertentu + partner = self.env['res.partner'].search([('name', 'ilike', 'BANGUNAN TEKNIK GRUP')], limit=1) + if not partner: + _logger.info("Partner tidak ditemukan.") + return + + invoices = self.env['account.move'].search([ + ('move_type', '=', 'out_invoice'), + ('state', '=', 'posted'), + ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), + ('invoice_date_due', 'in', target_dates), + ('partner_id', '=', partner.id), + ]) + + _logger.info(f"Invoices tahap 1: {invoices}") + + invoices = invoices.filtered( + lambda inv: inv.invoice_payment_term_id and 'tempo' in (inv.invoice_payment_term_id.name or '').lower() + ) + _logger.info(f"Invoices tahap 2: {invoices}") + + if not invoices: + _logger.info(f"Tidak ada invoice yang due untuk partner: {partner.name}") + return + + grouped = {} + for inv in invoices: + grouped.setdefault(inv.partner_id, []).append(inv) + + template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') + + for partner, invs in grouped.items(): + # Cari semua kontak anak yang reminder_invoices = True dan punya email + reminder_contacts = self.env['res.partner'].search([ + ('parent_id', '=', partner.id), + ('reminder_invoices', '=', True), + ('email', '!=', False), + ]) + + # Gabungkan partner.email utama + semua email dari child contact reminder + emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') + + if not emails: + _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") + continue + + email_to = ",".join(emails) + _logger.info(f"Email tujuan: {email_to}") + + # Buat isi tabel invoice + invoice_table_rows = "" + for inv in invs: + days_to_due = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 + invoice_table_rows += f""" + + {inv.name} + {fields.Date.to_string(inv.invoice_date) or '-'} + {fields.Date.to_string(inv.invoice_date_due) or '-'} + {days_to_due} + {formatLang(self.env, inv.amount_total, currency_obj=inv.currency_id)} + {inv.ref or '-'} + + """ + + subject = f"Reminder Invoice Due - {partner.name}" + body_html = re.sub( + r"]*>.*?", + f"{invoice_table_rows}", + template.body_html, + flags=re.DOTALL + ).replace('${object.name}', partner.name) \ + .replace('${object.partner_id.name}', partner.name) + + values = { + 'subject': subject, + 'email_to': email_to, + 'email_from': 'finance@indoteknik.co.id', + 'body_html': body_html, + 'reply_to': f'invoice+account.move_{invs[0].id}@indoteknik.co.id', + } + + _logger.info(f"Mengirim email ke: {email_to}") + template.send_mail(invs[0].id, force_send=True, email_values=values) + + # Post ke chatter + user_system = self.env['res.users'].browse(25) + system_id = user_system.partner_id.id if user_system else False + + for inv in invs: + inv.message_post( + subject=subject, + body=body_html, + subtype_id=self.env.ref('mail.mt_note').id, + author_id=system_id, + ) + + _logger.info(f"Reminder terkirim ke {partner.name} ({email_to}) → {len(invs)} invoice") @api.onchange('invoice_date') diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index d1d4ca02..f260f58e 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -28,7 +28,7 @@ class ResPartner(models.Model): supplier_ids = fields.Many2many('user.pengajuan.tempo.line', string="Suppliers") reminder_invoices = fields.Boolean( - string='Reminder Invoice', + string='Reminder Invoice?', help='Centang jika kontak ini harus menerima email pengingat invoice.', default=False ) -- cgit v1.2.3 From 60b037c72887d20e8cebd7f4a2439c546d16960b Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 29 Jul 2025 16:12:42 +0700 Subject: (andri) comment send due inv reminder --- indoteknik_custom/models/account_move.py | 214 +++++++++++++++---------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 14927bec..945a96f9 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -109,113 +109,113 @@ class AccountMove(models.Model): # result.append((move.id, move.display_name)) # return result - def send_due_invoice_reminder(self): - today = fields.Date.today() - target_dates = [ - today - timedelta(days=7), - today - timedelta(days=3), - today, - today + timedelta(days=3), - today + timedelta(days=7), - ] - - # Contoh khusus partner tertentu - partner = self.env['res.partner'].search([('name', 'ilike', 'BANGUNAN TEKNIK GRUP')], limit=1) - if not partner: - _logger.info("Partner tidak ditemukan.") - return - - invoices = self.env['account.move'].search([ - ('move_type', '=', 'out_invoice'), - ('state', '=', 'posted'), - ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), - ('invoice_date_due', 'in', target_dates), - ('partner_id', '=', partner.id), - ]) - - _logger.info(f"Invoices tahap 1: {invoices}") - - invoices = invoices.filtered( - lambda inv: inv.invoice_payment_term_id and 'tempo' in (inv.invoice_payment_term_id.name or '').lower() - ) - _logger.info(f"Invoices tahap 2: {invoices}") - - if not invoices: - _logger.info(f"Tidak ada invoice yang due untuk partner: {partner.name}") - return - - grouped = {} - for inv in invoices: - grouped.setdefault(inv.partner_id, []).append(inv) - - template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') - - for partner, invs in grouped.items(): - # Cari semua kontak anak yang reminder_invoices = True dan punya email - reminder_contacts = self.env['res.partner'].search([ - ('parent_id', '=', partner.id), - ('reminder_invoices', '=', True), - ('email', '!=', False), - ]) - - # Gabungkan partner.email utama + semua email dari child contact reminder - emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') - - if not emails: - _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") - continue - - email_to = ",".join(emails) - _logger.info(f"Email tujuan: {email_to}") - - # Buat isi tabel invoice - invoice_table_rows = "" - for inv in invs: - days_to_due = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 - invoice_table_rows += f""" - - {inv.name} - {fields.Date.to_string(inv.invoice_date) or '-'} - {fields.Date.to_string(inv.invoice_date_due) or '-'} - {days_to_due} - {formatLang(self.env, inv.amount_total, currency_obj=inv.currency_id)} - {inv.ref or '-'} - - """ - - subject = f"Reminder Invoice Due - {partner.name}" - body_html = re.sub( - r"]*>.*?", - f"{invoice_table_rows}", - template.body_html, - flags=re.DOTALL - ).replace('${object.name}', partner.name) \ - .replace('${object.partner_id.name}', partner.name) - - values = { - 'subject': subject, - 'email_to': email_to, - 'email_from': 'finance@indoteknik.co.id', - 'body_html': body_html, - 'reply_to': f'invoice+account.move_{invs[0].id}@indoteknik.co.id', - } - - _logger.info(f"Mengirim email ke: {email_to}") - template.send_mail(invs[0].id, force_send=True, email_values=values) - - # Post ke chatter - user_system = self.env['res.users'].browse(25) - system_id = user_system.partner_id.id if user_system else False - - for inv in invs: - inv.message_post( - subject=subject, - body=body_html, - subtype_id=self.env.ref('mail.mt_note').id, - author_id=system_id, - ) - - _logger.info(f"Reminder terkirim ke {partner.name} ({email_to}) → {len(invs)} invoice") + # def send_due_invoice_reminder(self): + # today = fields.Date.today() + # target_dates = [ + # today - timedelta(days=7), + # today - timedelta(days=3), + # today, + # today + timedelta(days=3), + # today + timedelta(days=7), + # ] + + # # Contoh khusus partner tertentu + # partner = self.env['res.partner'].search([('name', 'ilike', 'BANGUNAN TEKNIK GRUP')], limit=1) + # if not partner: + # _logger.info("Partner tidak ditemukan.") + # return + + # invoices = self.env['account.move'].search([ + # ('move_type', '=', 'out_invoice'), + # ('state', '=', 'posted'), + # ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), + # ('invoice_date_due', 'in', target_dates), + # ('partner_id', '=', partner.id), + # ]) + + # _logger.info(f"Invoices tahap 1: {invoices}") + + # invoices = invoices.filtered( + # lambda inv: inv.invoice_payment_term_id and 'tempo' in (inv.invoice_payment_term_id.name or '').lower() + # ) + # _logger.info(f"Invoices tahap 2: {invoices}") + + # if not invoices: + # _logger.info(f"Tidak ada invoice yang due untuk partner: {partner.name}") + # return + + # grouped = {} + # for inv in invoices: + # grouped.setdefault(inv.partner_id, []).append(inv) + + # template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') + + # for partner, invs in grouped.items(): + # # Cari semua kontak anak yang reminder_invoices = True dan punya email + # reminder_contacts = self.env['res.partner'].search([ + # ('parent_id', '=', partner.id), + # ('reminder_invoices', '=', True), + # ('email', '!=', False), + # ]) + + # # Gabungkan partner.email utama + semua email dari child contact reminder + # emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') + + # if not emails: + # _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") + # continue + + # email_to = ",".join(emails) + # _logger.info(f"Email tujuan: {email_to}") + + # # Buat isi tabel invoice + # invoice_table_rows = "" + # for inv in invs: + # days_to_due = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 + # invoice_table_rows += f""" + # + # {inv.name} + # {fields.Date.to_string(inv.invoice_date) or '-'} + # {fields.Date.to_string(inv.invoice_date_due) or '-'} + # {days_to_due} + # {formatLang(self.env, inv.amount_total, currency_obj=inv.currency_id)} + # {inv.ref or '-'} + # + # """ + + # subject = f"Reminder Invoice Due - {partner.name}" + # body_html = re.sub( + # r"]*>.*?", + # f"{invoice_table_rows}", + # template.body_html, + # flags=re.DOTALL + # ).replace('${object.name}', partner.name) \ + # .replace('${object.partner_id.name}', partner.name) + + # values = { + # 'subject': subject, + # 'email_to': email_to, + # 'email_from': 'finance@indoteknik.co.id', + # 'body_html': body_html, + # 'reply_to': f'invoice+account.move_{invs[0].id}@indoteknik.co.id', + # } + + # _logger.info(f"Mengirim email ke: {email_to}") + # template.send_mail(invs[0].id, force_send=True, email_values=values) + + # # Post ke chatter + # user_system = self.env['res.users'].browse(25) + # system_id = user_system.partner_id.id if user_system else False + + # for inv in invs: + # inv.message_post( + # subject=subject, + # body=body_html, + # subtype_id=self.env.ref('mail.mt_note').id, + # author_id=system_id, + # ) + + # _logger.info(f"Reminder terkirim ke {partner.name} ({email_to}) → {len(invs)} invoice") @api.onchange('invoice_date') -- cgit v1.2.3 From 09057a3d5074492dfd4de0a48644afb06391102d Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 4 Aug 2025 09:15:55 +0700 Subject: (andri) uncomment remnider --- indoteknik_custom/models/account_move.py | 214 +++++++++++++++---------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 945a96f9..14927bec 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -109,113 +109,113 @@ class AccountMove(models.Model): # result.append((move.id, move.display_name)) # return result - # def send_due_invoice_reminder(self): - # today = fields.Date.today() - # target_dates = [ - # today - timedelta(days=7), - # today - timedelta(days=3), - # today, - # today + timedelta(days=3), - # today + timedelta(days=7), - # ] - - # # Contoh khusus partner tertentu - # partner = self.env['res.partner'].search([('name', 'ilike', 'BANGUNAN TEKNIK GRUP')], limit=1) - # if not partner: - # _logger.info("Partner tidak ditemukan.") - # return - - # invoices = self.env['account.move'].search([ - # ('move_type', '=', 'out_invoice'), - # ('state', '=', 'posted'), - # ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), - # ('invoice_date_due', 'in', target_dates), - # ('partner_id', '=', partner.id), - # ]) - - # _logger.info(f"Invoices tahap 1: {invoices}") - - # invoices = invoices.filtered( - # lambda inv: inv.invoice_payment_term_id and 'tempo' in (inv.invoice_payment_term_id.name or '').lower() - # ) - # _logger.info(f"Invoices tahap 2: {invoices}") - - # if not invoices: - # _logger.info(f"Tidak ada invoice yang due untuk partner: {partner.name}") - # return - - # grouped = {} - # for inv in invoices: - # grouped.setdefault(inv.partner_id, []).append(inv) - - # template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') - - # for partner, invs in grouped.items(): - # # Cari semua kontak anak yang reminder_invoices = True dan punya email - # reminder_contacts = self.env['res.partner'].search([ - # ('parent_id', '=', partner.id), - # ('reminder_invoices', '=', True), - # ('email', '!=', False), - # ]) - - # # Gabungkan partner.email utama + semua email dari child contact reminder - # emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') - - # if not emails: - # _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") - # continue - - # email_to = ",".join(emails) - # _logger.info(f"Email tujuan: {email_to}") - - # # Buat isi tabel invoice - # invoice_table_rows = "" - # for inv in invs: - # days_to_due = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 - # invoice_table_rows += f""" - # - # {inv.name} - # {fields.Date.to_string(inv.invoice_date) or '-'} - # {fields.Date.to_string(inv.invoice_date_due) or '-'} - # {days_to_due} - # {formatLang(self.env, inv.amount_total, currency_obj=inv.currency_id)} - # {inv.ref or '-'} - # - # """ - - # subject = f"Reminder Invoice Due - {partner.name}" - # body_html = re.sub( - # r"]*>.*?", - # f"{invoice_table_rows}", - # template.body_html, - # flags=re.DOTALL - # ).replace('${object.name}', partner.name) \ - # .replace('${object.partner_id.name}', partner.name) - - # values = { - # 'subject': subject, - # 'email_to': email_to, - # 'email_from': 'finance@indoteknik.co.id', - # 'body_html': body_html, - # 'reply_to': f'invoice+account.move_{invs[0].id}@indoteknik.co.id', - # } - - # _logger.info(f"Mengirim email ke: {email_to}") - # template.send_mail(invs[0].id, force_send=True, email_values=values) - - # # Post ke chatter - # user_system = self.env['res.users'].browse(25) - # system_id = user_system.partner_id.id if user_system else False - - # for inv in invs: - # inv.message_post( - # subject=subject, - # body=body_html, - # subtype_id=self.env.ref('mail.mt_note').id, - # author_id=system_id, - # ) - - # _logger.info(f"Reminder terkirim ke {partner.name} ({email_to}) → {len(invs)} invoice") + def send_due_invoice_reminder(self): + today = fields.Date.today() + target_dates = [ + today - timedelta(days=7), + today - timedelta(days=3), + today, + today + timedelta(days=3), + today + timedelta(days=7), + ] + + # Contoh khusus partner tertentu + partner = self.env['res.partner'].search([('name', 'ilike', 'BANGUNAN TEKNIK GRUP')], limit=1) + if not partner: + _logger.info("Partner tidak ditemukan.") + return + + invoices = self.env['account.move'].search([ + ('move_type', '=', 'out_invoice'), + ('state', '=', 'posted'), + ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), + ('invoice_date_due', 'in', target_dates), + ('partner_id', '=', partner.id), + ]) + + _logger.info(f"Invoices tahap 1: {invoices}") + + invoices = invoices.filtered( + lambda inv: inv.invoice_payment_term_id and 'tempo' in (inv.invoice_payment_term_id.name or '').lower() + ) + _logger.info(f"Invoices tahap 2: {invoices}") + + if not invoices: + _logger.info(f"Tidak ada invoice yang due untuk partner: {partner.name}") + return + + grouped = {} + for inv in invoices: + grouped.setdefault(inv.partner_id, []).append(inv) + + template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') + + for partner, invs in grouped.items(): + # Cari semua kontak anak yang reminder_invoices = True dan punya email + reminder_contacts = self.env['res.partner'].search([ + ('parent_id', '=', partner.id), + ('reminder_invoices', '=', True), + ('email', '!=', False), + ]) + + # Gabungkan partner.email utama + semua email dari child contact reminder + emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') + + if not emails: + _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") + continue + + email_to = ",".join(emails) + _logger.info(f"Email tujuan: {email_to}") + + # Buat isi tabel invoice + invoice_table_rows = "" + for inv in invs: + days_to_due = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 + invoice_table_rows += f""" + + {inv.name} + {fields.Date.to_string(inv.invoice_date) or '-'} + {fields.Date.to_string(inv.invoice_date_due) or '-'} + {days_to_due} + {formatLang(self.env, inv.amount_total, currency_obj=inv.currency_id)} + {inv.ref or '-'} + + """ + + subject = f"Reminder Invoice Due - {partner.name}" + body_html = re.sub( + r"]*>.*?", + f"{invoice_table_rows}", + template.body_html, + flags=re.DOTALL + ).replace('${object.name}', partner.name) \ + .replace('${object.partner_id.name}', partner.name) + + values = { + 'subject': subject, + 'email_to': email_to, + 'email_from': 'finance@indoteknik.co.id', + 'body_html': body_html, + 'reply_to': f'invoice+account.move_{invs[0].id}@indoteknik.co.id', + } + + _logger.info(f"Mengirim email ke: {email_to}") + template.send_mail(invs[0].id, force_send=True, email_values=values) + + # Post ke chatter + user_system = self.env['res.users'].browse(25) + system_id = user_system.partner_id.id if user_system else False + + for inv in invs: + inv.message_post( + subject=subject, + body=body_html, + subtype_id=self.env.ref('mail.mt_note').id, + author_id=system_id, + ) + + _logger.info(f"Reminder terkirim ke {partner.name} ({email_to}) → {len(invs)} invoice") @api.onchange('invoice_date') -- cgit v1.2.3 From 1d58c7888d95793a4f393c9520b9d89a04f69c08 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 4 Aug 2025 11:56:48 +0700 Subject: (andri) fix template & logic send due --- indoteknik_custom/models/account_move.py | 58 +++++++++++++++++----- .../views/mail_template_invoice_reminder.xml | 10 ++-- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 14927bec..684f875d 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -119,8 +119,7 @@ class AccountMove(models.Model): today + timedelta(days=7), ] - # Contoh khusus partner tertentu - partner = self.env['res.partner'].search([('name', 'ilike', 'BANGUNAN TEKNIK GRUP')], limit=1) + partner = self.env['res.partner'].search([('name', 'ilike', 'SINAR SUKSES MANDIRI')], limit=1) if not partner: _logger.info("Partner tidak ditemukan.") return @@ -132,7 +131,6 @@ class AccountMove(models.Model): ('invoice_date_due', 'in', target_dates), ('partner_id', '=', partner.id), ]) - _logger.info(f"Invoices tahap 1: {invoices}") invoices = invoices.filtered( @@ -144,13 +142,15 @@ class AccountMove(models.Model): _logger.info(f"Tidak ada invoice yang due untuk partner: {partner.name}") return - grouped = {} + # Kelompokkan invoice berdasarkan partner & days_to_due + invoice_group = {} for inv in invoices: - grouped.setdefault(inv.partner_id, []).append(inv) + dtd = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 + invoice_group.setdefault((inv.partner_id, dtd), []).append(inv) template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') - for partner, invs in grouped.items(): + for (partner, dtd), invs in invoice_group.items(): # Cari semua kontak anak yang reminder_invoices = True dan punya email reminder_contacts = self.env['res.partner'].search([ ('parent_id', '=', partner.id), @@ -158,9 +158,7 @@ class AccountMove(models.Model): ('email', '!=', False), ]) - # Gabungkan partner.email utama + semua email dari child contact reminder emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') - if not emails: _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") continue @@ -168,7 +166,7 @@ class AccountMove(models.Model): email_to = ",".join(emails) _logger.info(f"Email tujuan: {email_to}") - # Buat isi tabel invoice + # Generate tabel invoice invoice_table_rows = "" for inv in invs: days_to_due = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 @@ -179,10 +177,42 @@ class AccountMove(models.Model): {fields.Date.to_string(inv.invoice_date_due) or '-'} {days_to_due} {formatLang(self.env, inv.amount_total, currency_obj=inv.currency_id)} + {inv.invoice_payment_term_id.name or '-'} + {inv.purchase_order_id.name or '-'} {inv.ref or '-'} """ + # Tentukan pesan berdasarkan sisa hari + days_to_due_message = "" + closing_message = "" + if dtd < 0: + days_to_due_message = f"Kami ingin mengingatkan bahwa tagihan anda akan jatuh tempo dalam {abs(dtd)} hari ke depan, dengan rincian sebagai berikut:" + closing_message = ( + "Kami mengharapkan pembayaran dapat dilakukan tepat waktu untuk mendukung kelancaran " + "hubungan kerja sama yang baik antara kedua belah pihak.
" + "Mohon konfirmasi apabila pembayaran telah dijadwalkan. " + "Terima kasih atas perhatian dan kerja samanya." + ) + + if dtd == 0: + days_to_due_message = "Kami ingin mengingatkan bahwa tagihan anda telah memasuki tanggal jatuh tempo pada hari ini, dengan rincian sebagai berikut:" + closing_message = ( + "Mohon kesediaannya untuk segera melakukan pembayaran tepat waktu guna menghindari status " + "keterlambatan dan menjaga kelancaran hubungan kerja sama yang telah terjalin dengan baik.
" + "Apabila pembayaran telah dijadwalkan atau diproses, mohon dapat dikonfirmasi kepada kami. " + "Terima kasih atas perhatian dan kerja samanya." + ) + + if dtd > 0: + days_to_due_message = f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {dtd}, dengan rincian sebagai berikut:" + closing_message = ( + "Mohon kesediaan Bapak/Ibu untuk segera melakukan pembayaran guna menghindari keterlambatan " + "dan menjaga kelancaran kerja sama yang telah terjalin dengan baik.
" + "Apabila pembayaran sudah dilakukan, mohon konfirmasi dan lampirkan bukti transfer agar dapat kami proses lebih lanjut. " + "Terima kasih atas perhatian dan kerja samanya." + ) + subject = f"Reminder Invoice Due - {partner.name}" body_html = re.sub( r"]*>.*?", @@ -190,14 +220,16 @@ class AccountMove(models.Model): template.body_html, flags=re.DOTALL ).replace('${object.name}', partner.name) \ - .replace('${object.partner_id.name}', partner.name) + .replace('${object.partner_id.name}', partner.name) \ + .replace('${days_to_due_message}', days_to_due_message) \ + .replace('${closing_message}', closing_message) values = { 'subject': subject, - 'email_to': email_to, + 'email_to': 'andrifebriyadiputra@gmail.com', 'email_from': 'finance@indoteknik.co.id', 'body_html': body_html, - 'reply_to': f'invoice+account.move_{invs[0].id}@indoteknik.co.id', + 'reply_to': 'finance@indoteknik.co.id', } _logger.info(f"Mengirim email ke: {email_to}") @@ -215,7 +247,7 @@ class AccountMove(models.Model): author_id=system_id, ) - _logger.info(f"Reminder terkirim ke {partner.name} ({email_to}) → {len(invs)} invoice") + _logger.info(f"Reminder terkirim ke {partner.name} ({email_to}) → {len(invs)} invoice (dtd = {dtd})") @api.onchange('invoice_date') diff --git a/indoteknik_custom/views/mail_template_invoice_reminder.xml b/indoteknik_custom/views/mail_template_invoice_reminder.xml index 21055eb0..6dfbe898 100644 --- a/indoteknik_custom/views/mail_template_invoice_reminder.xml +++ b/indoteknik_custom/views/mail_template_invoice_reminder.xml @@ -11,9 +11,9 @@

Dear ${object.name},

-

Berikut adalah daftar invoice Anda yang mendekati atau telah jatuh tempo:

+

${days_to_due_message}

- +
@@ -21,6 +21,8 @@ + + @@ -28,7 +30,8 @@
Invoice NumberJatuh Tempo Sisa Hari TotalPayment TermNo. PO Referensi
-

Mohon bantuan dan kerjasamanya agar tetap bisa bekerjasama dengan baik

+

${closing_message}

+

Terima Kasih.



@@ -42,6 +45,7 @@ +62-857-1697-0374 | finance@indoteknik.co.id

+

Email ini dikirim secara otomatis. Abaikan jika pembayaran telah dilakukan.

-- cgit v1.2.3 From 35c539953611d7e8968a23996df91738b2c6fc94 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 4 Aug 2025 12:48:11 +0700 Subject: testing --- indoteknik_custom/models/commision.py | 31 +++++++++++++++++++++++++++--- indoteknik_custom/models/cust_commision.py | 1 - 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 9f7df464..a900a399 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -399,6 +399,29 @@ class CustomerCommision(models.Model): # result = super(CustomerCommision, self).create(vals) # return result + def _fill_note_finance(self): + for rec in self: + if rec.status == 'approved' and rec.commision_type == 'fee': + fee_percent = rec.commision_percent or 0.0 + dpp = rec.total_dpp or 0.0 + + fee = dpp * fee_percent / 100 + pph21 = 0.5 * fee * 0.05 + fee_net = fee - pph21 + + rec.note_finance = ( + "Kelengkapan data penerima fee sudah lengkap (NPWP dan KTP)\n" + f"Perhitungan Fee ({fee_percent:.0f}%) dari nilai DPP pada Invoice terlampir sudah\n" + f"sesuai yaitu Rp {fee:,.0f}\n" + "Sesuai PMK No. 168 tahun 2023, komisi fee dikenakan PPH 21\n" + "sebesar :\n" + f"= 50% x Penghasilan Bruto x 5%\n" + f"= 50% x Rp {fee:,.0f} x 5%\n" + f"= Rp {pph21:,.0f}\n" + "Sehingga fee bersih sebesar\n" + f"= Rp {fee:,.0f} - Rp {pph21:,.0f}\n" + f"= Rp {fee_net:,.0f}" + ) def action_confirm_customer_commision(self): jakarta_tz = pytz.timezone('Asia/Jakarta') now = datetime.now(jakarta_tz) @@ -407,12 +430,12 @@ class CustomerCommision(models.Model): if not self.status or self.status == 'draft': self.status = 'pengajuan1' - elif self.status == 'pengajuan1' and self.env.user.id == 19: + elif self.status == 'pengajuan1': self.status = 'pengajuan2' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_sales = now_naive self.position_sales = 'Sales Manager' - elif self.status == 'pengajuan2' and self.env.user.id == 216: + elif self.status == 'pengajuan2': self.status = 'pengajuan3' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_marketing = now_naive @@ -422,9 +445,11 @@ class CustomerCommision(models.Model): self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_pimpinan = now_naive self.position_pimpinan = 'Pimpinan' - elif self.status == 'pengajuan4' and self.env.user.id == 1272: + elif self.status == 'pengajuan4': for line in self.commision_lines: line.invoice_id.is_customer_commision = True + if self.commision_type == 'fee': + self._fill_note_finance() self.status = 'approved' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_accounting = now_naive diff --git a/indoteknik_custom/models/cust_commision.py b/indoteknik_custom/models/cust_commision.py index c3105cfd..05c68935 100644 --- a/indoteknik_custom/models/cust_commision.py +++ b/indoteknik_custom/models/cust_commision.py @@ -34,4 +34,3 @@ class CustCommision(models.Model): for rec in duplicate_partner: if self.commision_type == rec.commision_type: raise UserError('Partner already exists') - \ No newline at end of file -- cgit v1.2.3 From 4dbaaade2793487477b4d83bfb76a838931707dd Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 4 Aug 2025 13:31:29 +0700 Subject: testing --- indoteknik_custom/models/commision.py | 42 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index a900a399..3f56b7f3 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -208,7 +208,7 @@ class CustomerCommision(models.Model): ('pending', 'Pending'), ('payment', 'Payment'), ], string='Payment Status', copy=False, readonly=True, tracking=3, default='pending') - note_finnance = fields.Text('Notes Finnance') + note_finnance = fields.Text('Notes Finance') reason_reject = fields.Char(string='Reason Reaject', tracking=True, track_visibility='onchange') approved_by = fields.Char(string='Approved By', tracking=True, track_visibility='always') @@ -401,27 +401,25 @@ class CustomerCommision(models.Model): def _fill_note_finance(self): for rec in self: - if rec.status == 'approved' and rec.commision_type == 'fee': - fee_percent = rec.commision_percent or 0.0 - dpp = rec.total_dpp or 0.0 - - fee = dpp * fee_percent / 100 - pph21 = 0.5 * fee * 0.05 - fee_net = fee - pph21 - - rec.note_finance = ( - "Kelengkapan data penerima fee sudah lengkap (NPWP dan KTP)\n" - f"Perhitungan Fee ({fee_percent:.0f}%) dari nilai DPP pada Invoice terlampir sudah\n" - f"sesuai yaitu Rp {fee:,.0f}\n" - "Sesuai PMK No. 168 tahun 2023, komisi fee dikenakan PPH 21\n" - "sebesar :\n" - f"= 50% x Penghasilan Bruto x 5%\n" - f"= 50% x Rp {fee:,.0f} x 5%\n" - f"= Rp {pph21:,.0f}\n" - "Sehingga fee bersih sebesar\n" - f"= Rp {fee:,.0f} - Rp {pph21:,.0f}\n" - f"= Rp {fee_net:,.0f}" - ) + fee_percent = rec.commision_percent or 0.0 + dpp = rec.total_dpp or 0.0 + + fee = dpp * fee_percent / 100 + pph21 = 0.5 * fee * 0.05 + fee_net = fee - pph21 + rec.note_finnance = ( + "Kelengkapan data penerima fee sudah lengkap (NPWP dan KTP)\n" + f"Perhitungan Fee ({fee_percent:.0f}%) dari nilai DPP pada Invoice terlampir sudah\n" + f"sesuai yaitu Rp {fee:,.0f}\n" + "Sesuai PMK No. 168 tahun 2023, komisi fee dikenakan PPH 21\n" + "sebesar :\n" + f"= 50% x Penghasilan Bruto x 5%\n" + f"= 50% x Rp {fee:,.0f} x 5%\n" + f"= Rp {pph21:,.0f}\n" + "Sehingga fee bersih sebesar\n" + f"= Rp {fee:,.0f} - Rp {pph21:,.0f}\n" + f"= Rp {fee_net:,.0f}" + ) def action_confirm_customer_commision(self): jakarta_tz = pytz.timezone('Asia/Jakarta') now = datetime.now(jakarta_tz) -- cgit v1.2.3 From 40cb418248b940bb28e5cc5938afed1c27eb7855 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 4 Aug 2025 13:36:58 +0700 Subject: fix --- indoteknik_custom/models/commision.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 3f56b7f3..6d538b83 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -428,12 +428,12 @@ class CustomerCommision(models.Model): if not self.status or self.status == 'draft': self.status = 'pengajuan1' - elif self.status == 'pengajuan1': + elif self.status == 'pengajuan1' and self.env.user.id == 19: self.status = 'pengajuan2' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_sales = now_naive self.position_sales = 'Sales Manager' - elif self.status == 'pengajuan2': + elif self.status == 'pengajuan2' and self.env.user.id == 216: self.status = 'pengajuan3' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_marketing = now_naive @@ -443,7 +443,7 @@ class CustomerCommision(models.Model): self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_pimpinan = now_naive self.position_pimpinan = 'Pimpinan' - elif self.status == 'pengajuan4': + elif self.status == 'pengajuan4' and self.env.user.id == 1272: for line in self.commision_lines: line.invoice_id.is_customer_commision = True if self.commision_type == 'fee': -- cgit v1.2.3 From 8386a9affb5e17b09ba76702f7fb01f070a5f713 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 4 Aug 2025 14:48:36 +0700 Subject: remove vendor bill val --- indoteknik_custom/models/tukar_guling_po.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 92d8c9a6..21f973a2 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -281,7 +281,7 @@ class TukarGulingPO(models.Model): def write(self, vals): if self.operations.picking_type_id.id not in [75, 28]: raise UserError("❌ Tidak bisa retur bukan BU/INPUT atau BU/PUT!") - self._check_bill_on_revisi_po() + # self._check_bill_on_revisi_po() tipe = vals.get('return_type', self.return_type) if self.operations and self.operations.picking_type_id.id == 28 and tipe == 'tukar_guling': @@ -349,7 +349,7 @@ class TukarGulingPO(models.Model): def action_submit(self): self.ensure_one() - self._check_bill_on_revisi_po() + # self._check_bill_on_revisi_po() self._validate_product_lines() self._check_not_allow_tukar_guling_on_bu_input() @@ -385,7 +385,7 @@ class TukarGulingPO(models.Model): def action_approve(self): self.ensure_one() self._validate_product_lines() - self._check_bill_on_revisi_po() + # self._check_bill_on_revisi_po() self._check_not_allow_tukar_guling_on_bu_input() if not self.operations: -- cgit v1.2.3 From 569fd1188a36aa97f0681bffe650dcbb9215f504 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 4 Aug 2025 15:05:50 +0700 Subject: remove vendor bill val --- indoteknik_custom/models/tukar_guling_po.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 21f973a2..467fff44 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -74,19 +74,19 @@ class TukarGulingPO(models.Model): return res - @api.constrains('return_type', 'operations') - def _check_bill_on_revisi_po(self): - for record in self: - if record.return_type == 'revisi_po' and record.origin: - bills = self.env['account.move'].search([ - ('invoice_origin', 'ilike', record.origin), - ('move_type', '=', 'in_invoice'), # hanya vendor bill - ('state', 'not in', ['draft', 'cancel']) - ]) - if bills: - raise ValidationError( - _("Tidak bisa memilih Return Type 'Revisi PO' karena PO %s sudah dibuat vendor bill.") % record.origin - ) + # @api.constrains('return_type', 'operations') + # def _check_bill_on_revisi_po(self): + # for record in self: + # if record.return_type == 'revisi_po' and record.origin: + # bills = self.env['account.move'].search([ + # ('invoice_origin', 'ilike', record.origin), + # ('move_type', '=', 'in_invoice'), # hanya vendor bill + # ('state', 'not in', ['draft', 'cancel']) + # ]) + # if bills: + # raise ValidationError( + # _("Tidak bisa memilih Return Type 'Revisi PO' karena PO %s sudah dibuat vendor bill.") % record.origin + # ) @api.onchange('operations') def _onchange_operations(self): -- cgit v1.2.3 From 9e783f7e3c356eb53f3afe10601a4eb9d0eeecdc Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 4 Aug 2025 15:23:35 +0700 Subject: (andri) fix template --- indoteknik_custom/models/account_move.py | 10 +++++----- indoteknik_custom/views/mail_template_invoice_reminder.xml | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 684f875d..b9315c43 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -119,7 +119,7 @@ class AccountMove(models.Model): today + timedelta(days=7), ] - partner = self.env['res.partner'].search([('name', 'ilike', 'SINAR SUKSES MANDIRI')], limit=1) + partner = self.env['res.partner'].search([('name', 'ilike', 'DIRGANTARA YUDHA ARTHA')], limit=1) if not partner: _logger.info("Partner tidak ditemukan.") return @@ -172,17 +172,17 @@ class AccountMove(models.Model): days_to_due = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 invoice_table_rows += f""" + {inv.partner_id.name} + {inv.purchase_order_id.name or '-'} {inv.name} {fields.Date.to_string(inv.invoice_date) or '-'} {fields.Date.to_string(inv.invoice_date_due) or '-'} - {days_to_due} {formatLang(self.env, inv.amount_total, currency_obj=inv.currency_id)} {inv.invoice_payment_term_id.name or '-'} - {inv.purchase_order_id.name or '-'} - {inv.ref or '-'} + {days_to_due} """ - + dadada = self.partner_id.name # Tentukan pesan berdasarkan sisa hari days_to_due_message = "" closing_message = "" diff --git a/indoteknik_custom/views/mail_template_invoice_reminder.xml b/indoteknik_custom/views/mail_template_invoice_reminder.xml index 6dfbe898..07f2122b 100644 --- a/indoteknik_custom/views/mail_template_invoice_reminder.xml +++ b/indoteknik_custom/views/mail_template_invoice_reminder.xml @@ -16,14 +16,14 @@ - - - - - - + - + + + + + + -- cgit v1.2.3 From 09032d721277221730efa3db7a2d8353524d509c Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 4 Aug 2025 16:02:11 +0700 Subject: (andri) check --- indoteknik_custom/models/account_move.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index b9315c43..53e5a8e8 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -158,6 +158,12 @@ class AccountMove(models.Model): ('email', '!=', False), ]) + _logger.info(f"Email Reminder Child {reminder_contacts}") + + if not reminder_contacts: + _logger.info(f"Partner {partner.name} tidak memiliki email yang sudah ceklis reminder") + continue + emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') if not emails: _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") @@ -205,7 +211,7 @@ class AccountMove(models.Model): ) if dtd > 0: - days_to_due_message = f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {dtd}, dengan rincian sebagai berikut:" + days_to_due_message = f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {dtd} hari, dengan rincian sebagai berikut:" closing_message = ( "Mohon kesediaan Bapak/Ibu untuk segera melakukan pembayaran guna menghindari keterlambatan " "dan menjaga kelancaran kerja sama yang telah terjalin dengan baik.
" -- cgit v1.2.3 From ced09a22623e17a3f1b12ba0d98fa5975b31bf51 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 4 Aug 2025 17:01:07 +0700 Subject: rev --- indoteknik_api/controllers/api_v1/sale_order.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index fd460ea0..2348a126 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -607,9 +607,6 @@ class SaleOrder(controller.Controller): if is_flash_sale_item: is_has_disc = True _logger.info("Item is flash sale product - marked as discounted") - elif discount_percent > 0 and not global_flash_sale: - is_has_disc = True - _logger.info(f"Item has discount {discount_percent}% - marked as discounted") elif global_flash_sale: _logger.info("Global flash sale active but item not eligible - not marked as discounted") -- cgit v1.2.3 From 519c18161bf658853908ddf7216aab504ac4c743 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 5 Aug 2025 10:53:05 +0700 Subject: add tracking on delivery amt --- 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 47018f52..94cfdc39 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -163,7 +163,7 @@ class SaleOrder(models.Model): carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method', tracking=3) have_visit_service = fields.Boolean(string='Have Visit Service', compute='_have_visit_service', help='To compute is customer get visit service') - delivery_amt = fields.Float(string='Delivery Amt', copy=False) + delivery_amt = fields.Float(string='Delivery Amt', copy=False, tracking=True) shipping_cost_covered = fields.Selection([ ('indoteknik', 'Indoteknik'), ('customer', 'Customer') -- cgit v1.2.3 From eb91fb8b9cf3ebc68f6c609acbc85015ce902cab Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 5 Aug 2025 11:24:34 +0700 Subject: add payment date on invoices --- indoteknik_custom/models/account_move.py | 12 ++++++++++++ indoteknik_custom/views/account_move.xml | 1 + 2 files changed, 13 insertions(+) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 1a6fad1c..929af949 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -94,6 +94,18 @@ class AccountMove(models.Model): compute='_compute_has_refund_so', ) + payment_date = fields.Date(string="Payment Date", compute='_compute_payment_date') + + def _compute_payment_date(self): + for move in self: + accountPayment = self.env['account.payment'] + + payment = accountPayment.search([]).filtered( + lambda p: move.id in p.reconciled_invoice_ids.ids + ) + + move.payment_date = payment[0].date + # def name_get(self): # result = [] # for move in self: diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml index 9b1c791b..23f2d931 100644 --- a/indoteknik_custom/views/account_move.xml +++ b/indoteknik_custom/views/account_move.xml @@ -29,6 +29,7 @@ + -- cgit v1.2.3 From d115f21b64e72af553d7c8965b9426b2802a0e3c Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 5 Aug 2025 11:52:32 +0700 Subject: fix bug --- indoteknik_custom/models/account_move.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 929af949..4be6c984 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -104,7 +104,10 @@ class AccountMove(models.Model): lambda p: move.id in p.reconciled_invoice_ids.ids ) - move.payment_date = payment[0].date + if payment: + move.payment_date = payment[0].date + else: + move.payment_date = False # def name_get(self): # result = [] -- cgit v1.2.3 From 9a94ae612d22a0dc329649e610810e7d9b6c3112 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 5 Aug 2025 14:05:49 +0700 Subject: fix bug api indoteknik delivery --- indoteknik_api/controllers/api_v1/stock_picking.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 85b0fbba..762e17c5 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -125,28 +125,33 @@ class StockPicking(controller.Controller): @http.route(prefix + 'stock-picking//documentation', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_partner_stock_picking_documentation(self, **kw): - scanid = int(kw.get('scanid', 0)) + scanid = kw.get('scanid', '').strip() sj_document = kw.get('sj_document', False) paket_document = kw.get('paket_document', False) - params = {'sj_documentation': sj_document, - 'paket_documentation': paket_document, - 'driver_arrival_date': datetime.utcnow(), - } + params = { + 'sj_documentation': sj_document, + 'paket_documentation': paket_document, + 'driver_arrival_date': datetime.utcnow(), + } - picking_data = request.env['stock.picking'].search([('id', '=', scanid)], limit=1) + picking_data = False + if scanid.isdigit() and int(scanid) < 2147483647: + picking_data = request.env['stock.picking'].search([('id', '=', int(scanid))], limit=1) if not picking_data: picking_data = request.env['stock.picking'].search([('picking_code', '=', scanid)], limit=1) if not picking_data: return self.response(code=404, description='picking not found') + 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 2d7aaf7fe387dd778731a2058dbe3210407ad803 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 5 Aug 2025 14:42:35 +0700 Subject: deactive redirect bu related --- indoteknik_custom/models/purchase_order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 27aca0d1..103a9131 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1083,9 +1083,9 @@ class PurchaseOrder(models.Model): # Tambahan: redirect ke BU hanya untuk single PO yang berhasil dikonfirmasi _logger.info("Jumlah PO: %s | State: %s", len(self), self.state) - if len(self) == 1: - _logger.info("Redirecting ke BU") - return self.action_view_related_bu() + # if len(self) == 1: + # _logger.info("Redirecting ke BU") + # return self.action_view_related_bu() return res -- cgit v1.2.3 From c42bdba2996d85d328897e42e7a1d86001b3a14d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 5 Aug 2025 15:21:56 +0700 Subject: multi update receipt date --- indoteknik_custom/__manifest__.py | 1 + indoteknik_custom/models/__init__.py | 1 + .../models/update_date_planned_po_wizard.py | 14 ++++++++++++ indoteknik_custom/security/ir.model.access.csv | 3 ++- indoteknik_custom/views/purchase_order.xml | 20 +++++++++++++++++ .../views/update_date_planned_po_wizard_view.xml | 25 ++++++++++++++++++++++ 6 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 indoteknik_custom/models/update_date_planned_po_wizard.py create mode 100644 indoteknik_custom/views/update_date_planned_po_wizard_view.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 2a4db273..d1ae681a 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -174,6 +174,7 @@ # 'views/tukar_guling_return_views.xml' 'views/tukar_guling_po.xml', # 'views/refund_sale_order.xml', + 'views/update_date_planned_po_wizard_view.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 51d25c1f..3a9f9312 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -156,3 +156,4 @@ from . import refund_sale_order # from . import patch from . import tukar_guling from . import tukar_guling_po +from . import update_date_planned_po_wizard \ No newline at end of file diff --git a/indoteknik_custom/models/update_date_planned_po_wizard.py b/indoteknik_custom/models/update_date_planned_po_wizard.py new file mode 100644 index 00000000..a0d241c8 --- /dev/null +++ b/indoteknik_custom/models/update_date_planned_po_wizard.py @@ -0,0 +1,14 @@ +from odoo import models, fields, api + +class PurchaseOrderUpdateDateWizard(models.TransientModel): + _name = 'purchase.order.update.date.wizard' + _description = 'Wizard to Update Receipt Date on Purchase Order Lines' + + date_planned = fields.Datetime(string="New Receipt Date", required=True) + + def action_update_date(self): + active_ids = self.env.context.get('active_ids', []) + orders = self.env['purchase.order'].browse(active_ids) + for order in orders: + order.write({'date_planned': self.date_planned}) + return {'type': 'ir.actions.act_window_close'} diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 9b43bf2a..d6e44f9d 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -191,4 +191,5 @@ access_tukar_guling_all_users,tukar.guling.all.users,model_tukar_guling,base.gro access_tukar_guling_line_all_users,tukar.guling.line.all.users,model_tukar_guling_line,base.group_user,1,1,1,1 access_tukar_guling_po_all_users,tukar.guling.po.all.users,model_tukar_guling_po,base.group_user,1,1,1,1 access_tukar_guling_line_po_all_users,tukar.guling.line.po.all.users,model_tukar_guling_line_po,base.group_user,1,1,1,1 -access_tukar_guling_mapping_koli_all_users,tukar.guling.mapping.koli.all.users,model_tukar_guling_mapping_koli,base.group_user,1,1,1,1 \ No newline at end of file +access_tukar_guling_mapping_koli_all_users,tukar.guling.mapping.koli.all.users,model_tukar_guling_mapping_koli,base.group_user,1,1,1,1 +access_purchase_order_update_date_wizard,access.purchase.order.update.date.wizard,model_purchase_order_update_date_wizard,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index fedcb4f9..15cdc788 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -428,4 +428,24 @@ action = records.open_form_multi_cancel() + + + Update Receipt Date + + + code + list + + action = { + 'type': 'ir.actions.act_window', + 'res_model': 'purchase.order.update.date.wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'active_ids': env.context.get('active_ids', []), + }, + } + + + \ No newline at end of file diff --git a/indoteknik_custom/views/update_date_planned_po_wizard_view.xml b/indoteknik_custom/views/update_date_planned_po_wizard_view.xml new file mode 100644 index 00000000..6b3ab991 --- /dev/null +++ b/indoteknik_custom/views/update_date_planned_po_wizard_view.xml @@ -0,0 +1,25 @@ + + + purchase.order.update.date.wizard.form + purchase.order.update.date.wizard + +
+ + + +
+
+ +
+
+ + + Update Receipt Date + purchase.order.update.date.wizard + form + new + +
+ \ No newline at end of file -- cgit v1.2.3 From 9f9081714356e87500ab05bc5a294e9ca9e526fe Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 6 Aug 2025 08:49:56 +0700 Subject: inv and bil return option --- indoteknik_custom/models/account_move.py | 1 + indoteknik_custom/models/tukar_guling.py | 41 ++++++++++++++++-- indoteknik_custom/models/tukar_guling_po.py | 65 ++++++++++++++++++++++------- indoteknik_custom/views/tukar_guling.xml | 3 ++ indoteknik_custom/views/tukar_guling_po.xml | 3 ++ 5 files changed, 93 insertions(+), 20 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 4be6c984..b03d0a80 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -18,6 +18,7 @@ _logger = logging.getLogger(__name__) class AccountMove(models.Model): _inherit = 'account.move' _description = 'Account Move' + tukar_guling_po_id = fields.Many2one('tukar.guling.po', string='Tukar Guling PO Ref') invoice_day_to_due = fields.Integer(string="Day to Due", compute="_compute_invoice_day_to_due") bill_day_to_due = fields.Date(string="Day to Due", compute="_compute_bill_day_to_due") date_send_fp = fields.Datetime(string="Tanggal Kirim Faktur Pajak") diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 3f81393a..d511779e 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -74,6 +74,38 @@ class TukarGuling(models.Model): date_sales = fields.Datetime('Approved Date Sales', tracking=3, readonly=True) date_logistic = fields.Datetime('Approved Date Logistic', tracking=3, readonly=True) + val_inv_opt = fields.Selection([ + ('tanpa_cancel', 'Tanpa Cancel Invoice'), + ('cancel_invoice', 'Cancel Invoice'), + ], tracking=3, string='Invoice Option') + + is_has_invoice = fields.Boolean('Has Invoice?', compute='_compute_is_has_invoice', readonly=True, default=False) + + invoice_id = fields.Many2one('account.move', string='Invoice Ref', readonly=True) + + @api.depends('origin') + def _compute_is_has_invoice(self): + for rec in self: + invoices = self.env['account.move'].search([ + ('invoice_origin', 'ilike', rec.origin), + ('move_type', '=', 'out_invoice'), # hanya invoice + ('state', 'not in', ['draft', 'cancel']) + ]) + if invoices: + rec.is_has_invoice = True + rec.invoice_id = invoices + else: + rec.is_has_invoice = False + + def set_opt(self): + if not self.val_inv_opt and self.is_has_invoice == True: + raise UserError("Kalau sudah ada invoice Return Invoice Option harus diisi!") + for rec in self: + if rec.val_inv_opt == 'cancel_invoice' and self.is_has_invoice == True: + raise UserError("Tidak bisa mengubah Return karena sudah ada invoice dan belum di cancel.") + elif rec.val_inv_opt == 'tanpa_cancel' and self.is_has_invoice == True: + continue + # @api.onchange('operations') # def get_partner_id(self): # if self.operations and self.operations.partner_id and self.operations.partner_id.name: @@ -288,7 +320,6 @@ class TukarGuling(models.Model): # ('state', '!=', 'cancel') # ]) > 0 - @api.constrains('return_type', 'operations') def _check_invoice_on_revisi_so(self): for record in self: if record.return_type == 'revisi_so' and record.origin: @@ -348,7 +379,7 @@ class TukarGuling(models.Model): self.ensure_one() if self.operations.picking_type_id.id not in [29, 30]: raise UserError("❌ Picking type harus BU/OUT atau BU/PICK") - self._check_invoice_on_revisi_so() + # self._check_invoice_on_revisi_so() operasi = self.operations.picking_type_id.id tipe = self.return_type pp = vals.get('return_type', tipe) @@ -455,7 +486,7 @@ class TukarGuling(models.Model): raise UserError( _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) - self._check_invoice_on_revisi_so() + # self._check_invoice_on_revisi_so() self._validate_product_lines() if self.state != 'draft': @@ -506,7 +537,7 @@ class TukarGuling(models.Model): def action_approve(self): self.ensure_one() self._validate_product_lines() - self._check_invoice_on_revisi_so() + # self._check_invoice_on_revisi_so() self._check_not_allow_tukar_guling_on_bu_pick() operasi = self.operations.picking_type_id.id @@ -546,6 +577,8 @@ class TukarGuling(models.Model): elif rec.state == 'approval_finance': if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") + rec._check_invoice_on_revisi_so() + rec.set_opt() rec.state = 'approval_logistic' rec.date_finance = now diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 467fff44..83aa49fe 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -54,6 +54,38 @@ class TukarGulingPO(models.Model): ('cancel', 'Cancel'), ], string='Status', default='draft', tracking=3) + val_bil_opt = fields.Selection([ + ('tanpa_cancel', 'Tanpa Cancel Bill'), + ('cancel_bill', 'Cancel Bill'), + ], tracking=3, string='Bill Option') + + is_has_bill = fields.Boolean('Has Bill?', compute='_compute_is_has_bill', readonly=True, default=False) + + bill_id = fields.Many2one('account.move', string='Bill Ref', readonly=True) + + @api.depends('origin') + def _compute_is_has_bill(self): + for rec in self: + bills = self.env['account.move'].search([ + ('invoice_origin', 'ilike', rec.origin), + ('move_type', '=', 'in_invoice'), # hanya vendor bill + ('state', 'not in', ['draft', 'cancel']) + ]) + if bills: + rec.is_has_bill = True + rec.bill_id = bills + else: + rec.is_has_bill = False + + def set_opt(self): + if not self.val_bil_opt and self.is_has_bill == True: + raise UserError("Kalau sudah ada bill Return Bill Option harus diisi!") + for rec in self: + if rec.val_bil_opt == 'cancel_bill' and self.is_has_bill == True: + raise UserError("Tidak bisa mengubah Return karena sudah ada bill dan belum di cancel.") + elif rec.val_bil_opt == 'tanpa_cancel' and self.is_has_bill == True: + continue + @api.model def create(self, vals): # Generate sequence number @@ -74,19 +106,18 @@ class TukarGulingPO(models.Model): return res - # @api.constrains('return_type', 'operations') - # def _check_bill_on_revisi_po(self): - # for record in self: - # if record.return_type == 'revisi_po' and record.origin: - # bills = self.env['account.move'].search([ - # ('invoice_origin', 'ilike', record.origin), - # ('move_type', '=', 'in_invoice'), # hanya vendor bill - # ('state', 'not in', ['draft', 'cancel']) - # ]) - # if bills: - # raise ValidationError( - # _("Tidak bisa memilih Return Type 'Revisi PO' karena PO %s sudah dibuat vendor bill.") % record.origin - # ) + def _check_bill_on_revisi_po(self): + for record in self: + if record.return_type == 'revisi_po' and record.origin: + bills = self.env['account.move'].search([ + ('invoice_origin', 'ilike', record.origin), + ('move_type', '=', 'in_invoice'), # hanya vendor bill + ('state', 'not in', ['draft', 'cancel']) + ]) + # if bills: + # raise ValidationError( + # _("Tidak bisa memilih Return Type 'Revisi PO' karena PO %s sudah dibuat vendor bill. Harus Cancel Jika ingin melanjutkan") % record.origin + # ) @api.onchange('operations') def _onchange_operations(self): @@ -400,19 +431,21 @@ class TukarGulingPO(models.Model): for rec in self: if rec.state == 'approval_purchase': if not rec.env.user.has_group('indoteknik_custom.group_role_sales'): - raise UserError("Hanya Sales Manager yang boleh approve tahap ini.") + raise UserError("Hanya Sales yang boleh approve tahap ini.") rec.state = 'approval_finance' rec.date_purchase = now elif rec.state == 'approval_finance': if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): - raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") + raise UserError("Hanya Finance yang boleh approve tahap ini.") + rec._check_bill_on_revisi_po() + rec.set_opt() rec.state = 'approval_logistic' rec.date_finance = now elif rec.state == 'approval_logistic': if not rec.env.user.has_group('indoteknik_custom.group_role_logistic'): - raise UserError("Hanya Logistic Manager yang boleh approve tahap ini.") + raise UserError("Hanya Logistic yang boleh approve tahap ini.") rec.state = 'approved' rec._create_pickings() rec.date_logistic = now diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index c23995d3..88e5b883 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -84,8 +84,11 @@ + + + diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml index accf7dbc..d3c41405 100644 --- a/indoteknik_custom/views/tukar_guling_po.xml +++ b/indoteknik_custom/views/tukar_guling_po.xml @@ -90,9 +90,12 @@ 'required': [('return_type', 'in', ['revisi_po', 'tukar_guling'])] }"/> + + + -- cgit v1.2.3 From ef5652dc2c57ef2648ea3730898e03c1c00c35f2 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 6 Aug 2025 08:56:32 +0700 Subject: wkwkw --- indoteknik_custom/models/tukar_guling_po.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 83aa49fe..72fb1607 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -460,7 +460,7 @@ class TukarGulingPO(models.Model): ('state', '=', 'done'), ('picking_type_id.id', '=', 76) ]) - if self.state == 'aproved' and prt: + if self.state == 'approved' and prt: self.state = 'done' # bu put rev po elif self.operations.picking_type_id.id == 75 and self.return_type == 'revisi_po': @@ -473,7 +473,7 @@ class TukarGulingPO(models.Model): ('state', '=', 'done'), ('picking_type_id.id', '=', 76) ]) - if self.state == 'aproved' and total_prt > 0 and prt == total_prt: + if self.state == 'approved' and total_prt > 0 and prt == total_prt: self.state = 'done' # bu put tukar guling elif self.operations.picking_type_id.id == 75 and self.return_type == 'tukar_guling': -- cgit v1.2.3 From 1db0c2f6148e5c13454cc25e4d4d877756536a4b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 6 Aug 2025 08:57:46 +0700 Subject: push --- indoteknik_custom/models/account_move.py | 1 - 1 file changed, 1 deletion(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index b03d0a80..4be6c984 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -18,7 +18,6 @@ _logger = logging.getLogger(__name__) class AccountMove(models.Model): _inherit = 'account.move' _description = 'Account Move' - tukar_guling_po_id = fields.Many2one('tukar.guling.po', string='Tukar Guling PO Ref') invoice_day_to_due = fields.Integer(string="Day to Due", compute="_compute_invoice_day_to_due") bill_day_to_due = fields.Date(string="Day to Due", compute="_compute_bill_day_to_due") date_send_fp = fields.Datetime(string="Tanggal Kirim Faktur Pajak") -- cgit v1.2.3 From fcd98b0f038cfa1653b8b12df77750253ebd7a68 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 6 Aug 2025 09:10:16 +0700 Subject: change api delivered --- indoteknik_api/controllers/api_v1/sale_order.py | 4 ++-- indoteknik_api/models/sale_order.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index fd460ea0..33b2a4e3 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -206,8 +206,8 @@ class SaleOrder(controller.Controller): ] total = len(bu_pickings) done_pickings = [p for p in bu_pickings if p.state == 'done'] - done_with_driver = [p for p in done_pickings if p.driver_arrival_date] - done_without_driver = [p for p in done_pickings if not p.driver_arrival_date] + done_with_driver = [p for p in done_pickings if p.sj_return_date] + done_without_driver = [p for p in done_pickings if not p.sj_return_date] if status == 'dikemas' and len(done_pickings) == 0: filtered_orders.append(sale_order) diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py index 615dcdcb..0561043b 100644 --- a/indoteknik_api/models/sale_order.py +++ b/indoteknik_api/models/sale_order.py @@ -67,8 +67,8 @@ class SaleOrder(models.Model): # Hitung status masing-masing picking total = len(bu_pickings) done_pickings = [p for p in bu_pickings if p.state == 'done'] - done_with_driver = [p for p in done_pickings if p.driver_arrival_date] - done_without_driver = [p for p in done_pickings if not p.driver_arrival_date] + done_with_driver = [p for p in done_pickings if p.sj_return_date] + done_without_driver = [p for p in done_pickings if not p.sj_return_date] if len(done_pickings) == 0: data['status'] = 'sale' -- cgit v1.2.3 From 9fb81ccccb8f2735ac5811d81b644b0ae70c4d8e Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 6 Aug 2025 10:33:25 +0700 Subject: push --- indoteknik_custom/models/tukar_guling.py | 24 ++++++++++++------------ indoteknik_custom/models/tukar_guling_po.py | 26 +++++++++++++------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index d511779e..3091acce 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -320,17 +320,17 @@ class TukarGuling(models.Model): # ('state', '!=', 'cancel') # ]) > 0 - def _check_invoice_on_revisi_so(self): - for record in self: - if record.return_type == 'revisi_so' and record.origin: - invoices = self.env['account.move'].search([ - ('invoice_origin', 'ilike', record.origin), - ('state', 'not in', ['draft', 'cancel']) - ]) - if invoices: - raise ValidationError( - _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen %s sudah dibuat invoice.") % record.origin - ) + # def _check_invoice_on_revisi_so(self): + # for record in self: + # if record.return_type == 'revisi_so' and record.origin: + # invoices = self.env['account.move'].search([ + # ('invoice_origin', 'ilike', record.origin), + # ('state', 'not in', ['draft', 'cancel']) + # ]) + # if invoices: + # raise ValidationError( + # _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen %s sudah dibuat invoice.") % record.origin + # ) @api.model def create(self, vals): @@ -577,7 +577,7 @@ class TukarGuling(models.Model): elif rec.state == 'approval_finance': if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") - rec._check_invoice_on_revisi_so() + # rec._check_invoice_on_revisi_so() rec.set_opt() rec.state = 'approval_logistic' rec.date_finance = now diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 72fb1607..38afaac6 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -106,18 +106,18 @@ class TukarGulingPO(models.Model): return res - def _check_bill_on_revisi_po(self): - for record in self: - if record.return_type == 'revisi_po' and record.origin: - bills = self.env['account.move'].search([ - ('invoice_origin', 'ilike', record.origin), - ('move_type', '=', 'in_invoice'), # hanya vendor bill - ('state', 'not in', ['draft', 'cancel']) - ]) - # if bills: - # raise ValidationError( - # _("Tidak bisa memilih Return Type 'Revisi PO' karena PO %s sudah dibuat vendor bill. Harus Cancel Jika ingin melanjutkan") % record.origin - # ) + # def _check_bill_on_revisi_po(self): + # for record in self: + # if record.return_type == 'revisi_po' and record.origin: + # bills = self.env['account.move'].search([ + # ('invoice_origin', 'ilike', record.origin), + # ('move_type', '=', 'in_invoice'), # hanya vendor bill + # ('state', 'not in', ['draft', 'cancel']) + # ]) + # if bills: + # raise ValidationError( + # _("Tidak bisa memilih Return Type 'Revisi PO' karena PO %s sudah dibuat vendor bill. Harus Cancel Jika ingin melanjutkan") % record.origin + # ) @api.onchange('operations') def _onchange_operations(self): @@ -438,7 +438,7 @@ class TukarGulingPO(models.Model): elif rec.state == 'approval_finance': if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): raise UserError("Hanya Finance yang boleh approve tahap ini.") - rec._check_bill_on_revisi_po() + # rec._check_bill_on_revisi_po() rec.set_opt() rec.state = 'approval_logistic' rec.date_finance = now -- cgit v1.2.3 From 837b7ba9f7118c6516ebad6b4022e1b449661c4d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 6 Aug 2025 11:01:45 +0700 Subject: fix payment date account move --- indoteknik_custom/models/account_move.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 4be6c984..595c5655 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -106,6 +106,8 @@ class AccountMove(models.Model): if payment: move.payment_date = payment[0].date + elif move.reklas_misc_id: + move.payment_date = move.reklas_misc_id.date else: move.payment_date = False -- cgit v1.2.3 From ab11c9455c6e125195ec5004adfd855058d46f5f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 6 Aug 2025 12:56:35 +0700 Subject: fix error singleton --- indoteknik_custom/models/tukar_guling.py | 2 +- indoteknik_custom/models/tukar_guling_po.py | 2 +- indoteknik_custom/views/tukar_guling.xml | 2 +- indoteknik_custom/views/tukar_guling_po.xml | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 3091acce..8de5b671 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -81,7 +81,7 @@ class TukarGuling(models.Model): is_has_invoice = fields.Boolean('Has Invoice?', compute='_compute_is_has_invoice', readonly=True, default=False) - invoice_id = fields.Many2one('account.move', string='Invoice Ref', readonly=True) + invoice_id = fields.Many2many('account.move', string='Invoice Ref', readonly=True) @api.depends('origin') def _compute_is_has_invoice(self): diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 38afaac6..5d444472 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -61,7 +61,7 @@ class TukarGulingPO(models.Model): is_has_bill = fields.Boolean('Has Bill?', compute='_compute_is_has_bill', readonly=True, default=False) - bill_id = fields.Many2one('account.move', string='Bill Ref', readonly=True) + bill_id = fields.Many2many('account.move', string='Bill Ref', readonly=True) @api.depends('origin') def _compute_is_has_bill(self): diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index 88e5b883..20aa2eef 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -85,7 +85,7 @@ attrs="{'readonly': [('state', 'not in', 'draft')]}"/> - + diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml index d3c41405..bc696a3d 100644 --- a/indoteknik_custom/views/tukar_guling_po.xml +++ b/indoteknik_custom/views/tukar_guling_po.xml @@ -91,7 +91,8 @@ }"/> - + + -- cgit v1.2.3 From 0114d5b1a20cf08ff162411cfc434087d36e2448 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 6 Aug 2025 14:18:35 +0700 Subject: api download quotation website --- indoteknik_api/controllers/api_v1/sale_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index d63f569b..6f5a3d44 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -424,8 +424,8 @@ class SaleOrder(controller.Controller): return self.response('Unauthorized') sale_order = request.env['sale.order'].sudo().search_read([('id', '=', id)], ['name']) - pdf, type = request.env['ir.actions.report'].sudo().search([('report_name', '=', 'quotation_so_new')]).render_jasper([id], {}) - # pdf, type = request.env['ir.actions.report'].sudo().search([('report_name', '=', 'indoteknik_custom.report_saleorder_website')])._render_qweb_pdf([id]) + # pdf, type = request.env['ir.actions.report'].sudo().search([('report_name', '=', 'quotation_so_new')]).render_jasper([id], {}) + pdf, type = request.env['ir.actions.report'].sudo().search([('report_name', '=', 'indoteknik_custom.report_saleorder_website')])._render_qweb_pdf([id]) if pdf and len(sale_order) > 0: return rest_api.response_attachment({ -- cgit v1.2.3 From cad9851dc642c877dcb1913e5bb12dc6405d9652 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 6 Aug 2025 14:19:32 +0700 Subject: fix when no bu pick --- indoteknik_custom/models/tukar_guling.py | 45 +++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 8de5b671..881021ab 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -479,13 +479,22 @@ class TukarGuling(models.Model): # raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") if self.operations.picking_type_id.id == 29: - for line in self.line_ids: - mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) - total_qty = sum(l.qty_return for l in mapping_lines) - if total_qty != line.product_uom_qty: - raise UserError( - _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) - + # Cek apakah ada BU/PICK di origin + origin = self.operations.origin + has_bu_pick = self.env['stock.picking'].search_count([ + ('origin', '=', origin), + ('picking_type_id', '=', 30), + ('state', '!=', 'cancel') + ]) > 0 + + if has_bu_pick: + for line in self.line_ids: + mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) + total_qty = sum(l.qty_return for l in mapping_lines) + if total_qty != line.product_uom_qty: + raise UserError( + _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name + ) # self._check_invoice_on_revisi_so() self._validate_product_lines() @@ -544,12 +553,22 @@ class TukarGuling(models.Model): tipe = self.return_type if self.operations.picking_type_id.id == 29: - for line in self.line_ids: - mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) - total_qty = sum(l.qty_return for l in mapping_lines) - if total_qty != line.product_uom_qty: - raise UserError( - _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name) + # Cek apakah ada BU/PICK di origin + origin = self.operations.origin + has_bu_pick = self.env['stock.picking'].search_count([ + ('origin', '=', origin), + ('picking_type_id', '=', 30), + ('state', '!=', 'cancel') + ]) > 0 + + if has_bu_pick: + for line in self.line_ids: + mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) + total_qty = sum(l.qty_return for l in mapping_lines) + if total_qty != line.product_uom_qty: + raise UserError( + _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name + ) if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") -- cgit v1.2.3 From 3ea10365d477bba3e5565dc636824a2b42bc8fca Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 6 Aug 2025 14:49:35 +0700 Subject: refactor due extension --- indoteknik_custom/models/account_move_due_extension.py | 9 +++++++++ indoteknik_custom/views/account_move_views.xml | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/account_move_due_extension.py b/indoteknik_custom/models/account_move_due_extension.py index 4a3f40e2..d354e3e3 100644 --- a/indoteknik_custom/models/account_move_due_extension.py +++ b/indoteknik_custom/models/account_move_due_extension.py @@ -33,6 +33,7 @@ class DueExtension(models.Model): counter = fields.Integer(string="Counter", compute='_compute_counter') approve_by = fields.Many2one('res.users', string="Approve By", readonly=True) date_approve = fields.Datetime(string="Date Approve", readonly=True) + def _compute_counter(self): for due in self: due.counter = due.partner_id.counter @@ -102,6 +103,14 @@ class DueExtension(models.Model): self.date_approve = datetime.utcnow() template = self.env.ref('indoteknik_custom.mail_template_due_extension_approve') template.send_mail(self.id, force_send=True) + return { + 'type': 'ir.actions.act_window', + 'res_model': 'sale.order', + 'view_mode': 'form', + 'res_id': self.order_id.id, + 'views': [(False, 'form')], + 'target': 'current', + } def generate_due_line(self): partners = self.partner_id.get_child_ids() diff --git a/indoteknik_custom/views/account_move_views.xml b/indoteknik_custom/views/account_move_views.xml index da25636e..0fd7c9cd 100644 --- a/indoteknik_custom/views/account_move_views.xml +++ b/indoteknik_custom/views/account_move_views.xml @@ -47,15 +47,20 @@
""" - dadada = self.partner_id.name - # Tentukan pesan berdasarkan sisa hari + days_to_due_message = "" closing_message = "" if dtd < 0: - days_to_due_message = f"Kami ingin mengingatkan bahwa tagihan anda akan jatuh tempo dalam {abs(dtd)} hari ke depan, dengan rincian sebagai berikut:" + days_to_due_message = ( + f"Kami ingin mengingatkan bahwa tagihan anda akan jatuh tempo dalam {abs(dtd)} hari ke depan, " + "dengan rincian sebagai berikut:" + ) closing_message = ( "Kami mengharapkan pembayaran dapat dilakukan tepat waktu untuk mendukung kelancaran " "hubungan kerja sama yang baik antara kedua belah pihak.
" @@ -227,7 +233,10 @@ class AccountMove(models.Model): ) if dtd == 0: - days_to_due_message = "Kami ingin mengingatkan bahwa tagihan anda telah memasuki tanggal jatuh tempo pada hari ini, dengan rincian sebagai berikut:" + days_to_due_message = ( + "Kami ingin mengingatkan bahwa tagihan anda telah memasuki tanggal jatuh tempo pada hari ini, " + "dengan rincian sebagai berikut:" + ) closing_message = ( "Mohon kesediaannya untuk segera melakukan pembayaran tepat waktu guna menghindari status " "keterlambatan dan menjaga kelancaran hubungan kerja sama yang telah terjalin dengan baik.
" @@ -236,7 +245,10 @@ class AccountMove(models.Model): ) if dtd > 0: - days_to_due_message = f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {dtd} hari, dengan rincian sebagai berikut:" + days_to_due_message = ( + f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {dtd} hari, " + "dengan rincian sebagai berikut:" + ) closing_message = ( "Mohon kesediaan Bapak/Ibu untuk segera melakukan pembayaran guna menghindari keterlambatan " "dan menjaga kelancaran kerja sama yang telah terjalin dengan baik.
" @@ -244,7 +256,6 @@ class AccountMove(models.Model): "Terima kasih atas perhatian dan kerja samanya." ) - subject = f"Reminder Invoice Due - {partner.name}" body_html = re.sub( r"]*>.*?", f"{invoice_table_rows}", @@ -255,15 +266,28 @@ class AccountMove(models.Model): .replace('${days_to_due_message}', days_to_due_message) \ .replace('${closing_message}', closing_message) + cc_list = [ + 'finance@indoteknik.co.id', + 'akbar@indoteknik.co.id', + 'stephan@indoteknik.co.id', + 'darren@indoteknik.co.id' + ] + sales_email = invs[0].invoice_user_id.partner_id.email if invs[0].invoice_user_id else None + if sales_email and sales_email not in cc_list: + cc_list.append(sales_email) + + # Siapkan email values values = { - 'subject': subject, - 'email_to': 'andrifebriyadiputra@gmail.com', + 'subject': f"Reminder Invoice Due - {partner.name}", + # 'email_to': 'andrifebriyadiputra@gmail.com', + 'email_to': email_to, 'email_from': 'finance@indoteknik.co.id', + 'email_cc': ",".join(cc_list), 'body_html': body_html, 'reply_to': 'finance@indoteknik.co.id', } - _logger.info(f"Mengirim email ke: {email_to}") + _logger.info(f"Mengirim email ke: {values['email_to']} CC: {values['email_cc']}") template.send_mail(invs[0].id, force_send=True, email_values=values) # Post ke chatter @@ -272,13 +296,13 @@ class AccountMove(models.Model): for inv in invs: inv.message_post( - subject=subject, + subject=values['subject'], body=body_html, subtype_id=self.env.ref('mail.mt_note').id, author_id=system_id, ) - _logger.info(f"Reminder terkirim ke {partner.name} ({email_to}) → {len(invs)} invoice (dtd = {dtd})") + _logger.info(f"Reminder terkirim ke {partner.name} ({values['email_to']}) → {len(invs)} invoice (dtd = {dtd})") @api.onchange('invoice_date') diff --git a/indoteknik_custom/views/mail_template_invoice_reminder.xml b/indoteknik_custom/views/mail_template_invoice_reminder.xml index 07f2122b..8450be28 100644 --- a/indoteknik_custom/views/mail_template_invoice_reminder.xml +++ b/indoteknik_custom/views/mail_template_invoice_reminder.xml @@ -6,7 +6,7 @@ Reminder Invoice Due - ${object.name} finance@indoteknik.co.id - andrifebriyadiputra@gmail.com +

Dear ${object.name},

-- cgit v1.2.3 From be4952c12d51106b4cffd6b7de1be09562829331 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 8 Aug 2025 16:55:59 +0700 Subject: (andri) fix approval --- indoteknik_custom/models/sale_order.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 5eb90d83..125a049b 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2528,6 +2528,9 @@ class SaleOrder(models.Model): if user.is_leader or user.is_sales_manager: return True + if user.id in (3401, 20, 3988): # admin (fida, nabila, ninda) + return True + if not self.team_id or not self.team_id.user_id: return True -- cgit v1.2.3 From 4303bc704835a48a3005d409f564030a2d53befc Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 8 Aug 2025 17:19:02 +0700 Subject: (andri) fix --- indoteknik_custom/models/sale_order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 125a049b..e71e3830 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2160,9 +2160,9 @@ class SaleOrder(models.Model): # return self._create_notification_action('Notification', # 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') - if not order._is_request_to_own_team_leader(): + if not order.with_context(ask_approval=True)._is_request_to_own_team_leader(): return self._create_notification_action( - 'Peringatan', + 'Peringatan', 'Hanya bisa konfirmasi SO tim Anda.' ) if order._requires_approval_margin_leader(): @@ -2531,7 +2531,7 @@ class SaleOrder(models.Model): if user.id in (3401, 20, 3988): # admin (fida, nabila, ninda) return True - if not self.team_id or not self.team_id.user_id: + if self.env.context.get("ask_approval") and user.id in (3401, 20, 3988): return True salesperson_id = self.user_id.id -- cgit v1.2.3 From 06b661f2fa9e07e4b3d1d2b5c7aa9f16f057a3a3 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Sat, 9 Aug 2025 11:57:27 +0700 Subject: fix bug --- indoteknik_custom/models/stock_picking.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 46bb6cee..82f81642 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1059,10 +1059,13 @@ class StockPicking(models.Model): return self.sale_id.date_doc_kirim = self.date_doc_kirim - from odoo import fields - def action_assign(self): - pickings_to_assign = self.filtered(lambda p: not (p.sale_id and p.sale_id.hold_outgoing)) + if self.env.context.get('default_picking_type_id'): + pickings_to_assign = self.filtered( + lambda p: not (p.sale_id and p.sale_id.hold_outgoing) + ) + else: + pickings_to_assign = self res = super(StockPicking, pickings_to_assign).action_assign() -- cgit v1.2.3 From b6ae7b2c9f1c564f3bf2a471f4871fda745d215d Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 9 Aug 2025 13:59:11 +0700 Subject: origin so can be clicked --- indoteknik_custom/models/tukar_guling.py | 27 +++++++++++++++++++++------ indoteknik_custom/views/tukar_guling.xml | 5 +++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 4c7722d5..6aedb70e 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -32,7 +32,7 @@ class TukarGuling(models.Model): 'tukar_guling_id', string='Transfers' ) - # origin_so = fields.Many2one('sale.order', string='Origin SO') + origin_so = fields.Many2one('sale.order', string='Origin SO', compute='_compute_origin_so') name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) operations = fields.Many2one( @@ -83,6 +83,15 @@ class TukarGuling(models.Model): invoice_id = fields.Many2many('account.move', string='Invoice Ref', readonly=True) + @api.depends('origin', 'operations') + def _compute_origin_so(self): + for rec in self: + rec.origin_so = False + origin_str = rec.origin or rec.operations.origin + if origin_str: + so = self.env['sale.order'].search([('name', '=', origin_str)], limit=1) + rec.origin_so = so.id if so else False + @api.depends('origin') def _compute_is_has_invoice(self): for rec in self: @@ -144,8 +153,6 @@ class TukarGuling(models.Model): if self.line_ids and from_return_picking: # Hanya update origin, jangan ubah lines - if self.operations.origin: - self.origin = self.operations.origin _logger.info("📌 Menggunakan product lines dari return wizard, tidak populate ulang.") # 🚀 Tapi tetap populate mapping koli jika BU/OUT @@ -177,6 +184,7 @@ class TukarGuling(models.Model): # Set origin dari operations if self.operations.origin: self.origin = self.operations.origin + self.origin_so = self.operations.group_id.id # Auto-populate lines dari move_ids operations lines_data = [] @@ -332,17 +340,20 @@ class TukarGuling(models.Model): # _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen %s sudah dibuat invoice.") % record.origin # ) + @api.model def create(self, vals): - # Generate sequence number if not vals.get('name') or vals['name'] == 'New': vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') - # Auto-fill origin from operations - if not vals.get('origin') and vals.get('operations'): + if vals.get('operations'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: vals['origin'] = picking.origin + # Find matching SO + so = self.env['sale.order'].search([('name', '=', picking.origin)], limit=1) + if so: + vals['origin_so'] = so.id if picking.partner_id: vals['partner_id'] = picking.partner_id.id @@ -350,6 +361,10 @@ class TukarGuling(models.Model): res.message_post(body=_("CCM Created By %s") % self.env.user.name) return res + res = super(TukarGuling, self).create(vals) + res.message_post(body=_("CCM Created By %s") % self.env.user.name) + return res + def copy(self, default=None): if default is None: default = {} diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index 20aa2eef..a8d8b7b7 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -21,7 +21,7 @@ pengajuan.tukar.guling.tree tukar.guling - + @@ -83,7 +83,8 @@ - + + -- cgit v1.2.3 From ba20997c842be46b6759aad7d4dfa3736c522982 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 9 Aug 2025 14:35:00 +0700 Subject: origin po can be clicked --- indoteknik_custom/models/tukar_guling_po.py | 11 +++++++++++ indoteknik_custom/views/tukar_guling_po.xml | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 0badc117..03d7668f 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -62,6 +62,16 @@ class TukarGulingPO(models.Model): is_has_bill = fields.Boolean('Has Bill?', compute='_compute_is_has_bill', readonly=True, default=False) bill_id = fields.Many2many('account.move', string='Bill Ref', readonly=True) + origin_po = fields.Many2one('purchase.order', string='Origin PO', compute='_compute_origin_po') + + @api.depends('origin', 'operations') + def _compute_origin_po(self): + for rec in self: + rec.origin_po = False + origin_str = rec.origin or rec.operations.origin + if origin_str: + so = self.env['purchase.order'].search([('name', '=', origin_str)], limit=1) + rec.origin_po = so.id if so else False @api.depends('origin') def _compute_is_has_bill(self): @@ -133,6 +143,7 @@ class TukarGulingPO(models.Model): # Hanya update origin, jangan ubah lines if self.operations.origin: self.origin = self.operations.origin + self.origin_po = self.operations.group_id.id return if from_return_picking: diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml index bc696a3d..739a4f9c 100644 --- a/indoteknik_custom/views/tukar_guling_po.xml +++ b/indoteknik_custom/views/tukar_guling_po.xml @@ -89,11 +89,11 @@ attrs="{ 'required': [('return_type', 'in', ['revisi_po', 'tukar_guling'])] }"/> - + + - -- cgit v1.2.3 From df5ff0fc9bcd45c19b74288d8e5cfee018ba5bdd Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 9 Aug 2025 14:37:37 +0700 Subject: typo --- indoteknik_custom/views/tukar_guling_po.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml index 739a4f9c..d0ae9e96 100644 --- a/indoteknik_custom/views/tukar_guling_po.xml +++ b/indoteknik_custom/views/tukar_guling_po.xml @@ -24,7 +24,7 @@ - + Date: Sun, 10 Aug 2025 19:42:25 +0700 Subject: show invoice rev --- indoteknik_custom/models/tukar_guling.py | 32 ++++++++++++++++++++++++-------- indoteknik_custom/views/tukar_guling.xml | 4 +++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 6aedb70e..624de7a9 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -92,19 +92,35 @@ class TukarGuling(models.Model): so = self.env['sale.order'].search([('name', '=', origin_str)], limit=1) rec.origin_so = so.id if so else False - @api.depends('origin') + @api.depends('origin_so', 'mapping_koli_ids.product_id', 'line_ids.product_id') def _compute_is_has_invoice(self): for rec in self: - invoices = self.env['account.move'].search([ - ('invoice_origin', 'ilike', rec.origin), - ('move_type', '=', 'out_invoice'), # hanya invoice - ('state', 'not in', ['draft', 'cancel']) - ]) + rec.is_has_invoice = False + rec.invoice_id = False + + if not rec.origin_so: + continue + + # Ambil produk dari mapping_koli kalau ada, kalau kosong pakai line_ids + if rec.mapping_koli_ids: + retur_products = rec.mapping_koli_ids.mapped('product_id') + else: + retur_products = rec.line_ids.mapped('product_id') + + # Semua invoice dari SO asal + invoices = rec.origin_so.invoice_ids.filtered( + lambda inv: inv.state not in ('draft', 'cancel') + and inv.move_type in ('out_invoice', 'out_refund') + ) + + # Filter invoice yang punya produk yang diretur + invoices = invoices.filtered( + lambda inv: any(line.product_id in retur_products for line in inv.invoice_line_ids) + ) + if invoices: rec.is_has_invoice = True rec.invoice_id = invoices - else: - rec.is_has_invoice = False def set_opt(self): if not self.val_inv_opt and self.is_has_invoice == True: diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index a8d8b7b7..9dd31905 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -86,7 +86,6 @@ - @@ -96,6 +95,9 @@ + + + -- cgit v1.2.3 From 356e53b85511c98cf4c942c32f2f370f58c9d849 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sun, 10 Aug 2025 20:16:09 +0700 Subject: pengajuan return so revert --- indoteknik_custom/models/tukar_guling.py | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 624de7a9..6aedb70e 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -92,35 +92,19 @@ class TukarGuling(models.Model): so = self.env['sale.order'].search([('name', '=', origin_str)], limit=1) rec.origin_so = so.id if so else False - @api.depends('origin_so', 'mapping_koli_ids.product_id', 'line_ids.product_id') + @api.depends('origin') def _compute_is_has_invoice(self): for rec in self: - rec.is_has_invoice = False - rec.invoice_id = False - - if not rec.origin_so: - continue - - # Ambil produk dari mapping_koli kalau ada, kalau kosong pakai line_ids - if rec.mapping_koli_ids: - retur_products = rec.mapping_koli_ids.mapped('product_id') - else: - retur_products = rec.line_ids.mapped('product_id') - - # Semua invoice dari SO asal - invoices = rec.origin_so.invoice_ids.filtered( - lambda inv: inv.state not in ('draft', 'cancel') - and inv.move_type in ('out_invoice', 'out_refund') - ) - - # Filter invoice yang punya produk yang diretur - invoices = invoices.filtered( - lambda inv: any(line.product_id in retur_products for line in inv.invoice_line_ids) - ) - + invoices = self.env['account.move'].search([ + ('invoice_origin', 'ilike', rec.origin), + ('move_type', '=', 'out_invoice'), # hanya invoice + ('state', 'not in', ['draft', 'cancel']) + ]) if invoices: rec.is_has_invoice = True rec.invoice_id = invoices + else: + rec.is_has_invoice = False def set_opt(self): if not self.val_inv_opt and self.is_has_invoice == True: -- cgit v1.2.3 From 0278f49a68fb9a9ce8844d3ecd9f56232710be38 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sun, 10 Aug 2025 20:17:12 +0700 Subject: change bill view --- indoteknik_custom/views/tukar_guling_po.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml index d0ae9e96..1c6a86ea 100644 --- a/indoteknik_custom/views/tukar_guling_po.xml +++ b/indoteknik_custom/views/tukar_guling_po.xml @@ -93,7 +93,6 @@ - @@ -103,6 +102,9 @@ + + + -- cgit v1.2.3 From 7a329ad669cb9fe630a52651a438f9bb5f294c40 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 11 Aug 2025 08:33:26 +0700 Subject: (andri) fix reminder --- indoteknik_custom/models/account_move.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index fd08ed60..7a331791 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -190,11 +190,16 @@ class AccountMove(models.Model): ]) _logger.info(f"Email Reminder Child {reminder_contacts}") - if not reminder_contacts: - _logger.info(f"Partner {partner.name} tidak memiliki email yang sudah ceklis reminder") - continue + # if not reminder_contacts: + # _logger.info(f"Partner {partner.name} tidak memiliki email yang sudah ceklis reminder") + # continue emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') + if reminder_contacts: + _logger.info(f"Email Reminder Child {reminder_contacts}") + else: + _logger.info(f"Tidak ada child contact reminder, gunakan email utama partner") + if not emails: _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") continue -- cgit v1.2.3 From a929da36d56ad080e50adeb331d2796c0c8c13ed Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 11 Aug 2025 10:15:19 +0700 Subject: (andri) fix approval sales --- 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 e71e3830..45814475 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2178,7 +2178,7 @@ class SaleOrder(models.Model): self.check_product_bom() self.check_credit_limit() self.check_limit_so_to_invoice() - order.approval_status = 'approved' + # order.approval_status = 'approved' return self._create_approval_notification('Team Sales') raise UserError("Bisa langsung Confirm") -- cgit v1.2.3 From bbd7fbf19ef974b4f7716671e3d6203a15a2f2c3 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 11 Aug 2025 11:40:57 +0700 Subject: (andri) add flag & temp di autoset shipping from website & estimate biteship --- indoteknik_custom/models/sale_order.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 45814475..b52df54c 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1116,8 +1116,9 @@ class SaleOrder(models.Model): break if not selected_option and shipping_options: + if not self.env.context.get('from_website_checkout'): + _logger.info(f"[DEFAULT] Tidak ada yang cocok, pakai opsi pertama: {shipping_options[0].name}") selected_option = shipping_options[0] - _logger.info(f"[DEFAULT] Tidak ada yang cocok, pakai opsi pertama: {selected_option.name}") # ❗ Ganti carrier_id hanya jika BELUM terisi sama sekali (contoh: user dari backend) if not self.carrier_id: @@ -1137,9 +1138,18 @@ class SaleOrder(models.Model): # Set shipping option dan nilai ongkir if selected_option: - self.shipping_option_id = selected_option.id - self.delivery_amt = selected_option.price - self.delivery_service_type = selected_option.courier_service_code + if self.env.context.get('from_website_checkout'): + # Simpan di context sebagai nilai sementara + self = self.with_context( + _temp_delivery_amt=selected_option.price, + _temp_delivery_service=selected_option.courier_service_code, + _temp_shipping_option=selected_option.id + ) + else: + self.shipping_option_id = selected_option.id + self.delivery_amt = selected_option.price + self.delivery_service_type = selected_option.courier_service_code + message_lines = [f"Estimasi Ongkos Kirim Biteship:
"] for courier, options in courier_options.items(): @@ -2950,6 +2960,14 @@ class SaleOrder(models.Model): order.select_shipping_option = 'biteship' order.action_estimate_shipping() + temp_price = self.env.context.get('_temp_delivery_amt') + temp_service = self.env.context.get('_temp_delivery_service') + temp_option_id = self.env.context.get('_temp_shipping_option') + if temp_price and temp_option_id: + order.shipping_option_id = temp_option_id + order.delivery_amt = temp_price + order.delivery_service_type = temp_service + # Restore pilihan user setelah estimasi if user_carrier_id and user_service: # Dapatkan provider -- cgit v1.2.3 From 74e2ec630680c2e9a679f837bd1278a7d0a838de Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 11 Aug 2025 12:52:28 +0700 Subject: (andri) fix reminder inv --- indoteknik_custom/models/account_move.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 7a331791..85ded4cd 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -97,6 +97,8 @@ class AccountMove(models.Model): payment_date = fields.Date(string="Payment Date", compute='_compute_payment_date') partial_payment = fields.Float(string="Partial Payment", compute='compute_partial_payment') + reminder_sent_date = fields.Date(string="Tanggal Reminder Terkirim") + def compute_partial_payment(self): for move in self: if move.amount_total_signed > 0 and move.amount_residual_signed > 0 and move.payment_state == 'partial': @@ -177,11 +179,18 @@ class AccountMove(models.Model): invoice_group = {} for inv in invoices: dtd = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 - invoice_group.setdefault((inv.partner_id, dtd), []).append(inv) + key = (inv.partner_id, dtd) + if key not in invoice_group: + invoice_group[key] = self.env['account.move'] # recordset kosong + invoice_group[key] |= inv # gabung recordset template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') for (partner, dtd), invs in invoice_group.items(): + if all(inv.reminder_sent_date == today for inv in invs): + _logger.info(f"Reminder untuk {partner.name} sudah terkirim hari ini, skip.") + continue + # Ambil child contact yang di-checklist reminder_invoices reminder_contacts = self.env['res.partner'].search([ ('parent_id', '=', partner.id), @@ -213,7 +222,7 @@ class AccountMove(models.Model): invoice_table_rows += f"""
- + @@ -225,9 +234,9 @@ class AccountMove(models.Model): days_to_due_message = "" closing_message = "" - if dtd < 0: + if dtd > 0: days_to_due_message = ( - f"Kami ingin mengingatkan bahwa tagihan anda akan jatuh tempo dalam {abs(dtd)} hari ke depan, " + f"Kami ingin mengingatkan bahwa tagihan anda akan jatuh tempo dalam {dtd} hari ke depan, " "dengan rincian sebagai berikut:" ) closing_message = ( @@ -249,9 +258,9 @@ class AccountMove(models.Model): "Terima kasih atas perhatian dan kerja samanya." ) - if dtd > 0: + if dtd < 0: days_to_due_message = ( - f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {dtd} hari, " + f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {abs(dtd)} hari, " "dengan rincian sebagai berikut:" ) closing_message = ( @@ -292,13 +301,13 @@ class AccountMove(models.Model): 'reply_to': 'finance@indoteknik.co.id', } - _logger.info(f"Mengirim email ke: {values['email_to']} CC: {values['email_cc']}") + _logger.info(f"Mengirim email ke: {values['email_to']} > email CC: {values['email_cc']}") template.send_mail(invs[0].id, force_send=True, email_values=values) - + # flag + invs.write({'reminder_sent_date': today}) # Post ke chatter user_system = self.env['res.users'].browse(25) system_id = user_system.partner_id.id if user_system else False - for inv in invs: inv.message_post( subject=values['subject'], -- cgit v1.2.3 From 8cda2bffdae2ad10c32421958e0114a5f3a9d6e1 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 11 Aug 2025 12:54:54 +0700 Subject: (andri) fix --- indoteknik_custom/models/account_move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 85ded4cd..273bcdf9 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -296,7 +296,7 @@ class AccountMove(models.Model): # 'email_to': 'andrifebriyadiputra@gmail.com', 'email_to': email_to, 'email_from': 'finance@indoteknik.co.id', - 'email_cc': ",".join(cc_list), + 'email_cc': ",".join(sorted(set(cc_list))), 'body_html': body_html, 'reply_to': 'finance@indoteknik.co.id', } -- cgit v1.2.3 From 040e2830f7d265df34bf6036939d3b53051e153e Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 12 Aug 2025 10:59:35 +0700 Subject: revisi transaction detail --- indoteknik_api/controllers/api_v1/partner.py | 4 ++-- indoteknik_api/controllers/api_v1/sale_order.py | 21 +++++++++++++++++++++ indoteknik_api/models/sale_order.py | 10 ++++++++++ indoteknik_custom/models/stock_picking.py | 8 +++++--- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index b8bd21be..14136ca3 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -150,7 +150,7 @@ class Partner(controller.Controller): partner_params = self.get_request_params(request_data, { 'tax_name': ['alias:nama_wajib_pajak'], - 'company_type_id': ['number'], + # 'company_type_id': ['number'], 'industry_id': ['number'], 'npwp': [], 'alamat_lengkap_text': [], @@ -170,7 +170,7 @@ class Partner(controller.Controller): if 'id_user' in request_data: user_params = self.get_request_params(request_data, { 'id_user': ['required', 'number'], - 'company_type_id': ['number'], + # 'company_type_id': ['number'], 'industry_id': ['number'], 'tax_name': ['alias:nama_wajib_pajak'], 'npwp': [], diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 6f5a3d44..d199cd84 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -87,6 +87,8 @@ class SaleOrder(controller.Controller): 'amount_tax': sale.amount_tax, 'amount_total': sale.amount_total, 'expected_ready_to_ship': f"{sale.expected_ready_to_ship.day} {INDONESIAN_MONTHS[sale.expected_ready_to_ship.month]} {sale.expected_ready_to_ship.year}", + 'eta_date_start': f"{sale.eta_date_start.day} {INDONESIAN_MONTHS[sale.eta_date_start.month]} {sale.eta_date_start.year}", + 'eta_date_end': f"{sale.eta_date.day} {INDONESIAN_MONTHS[sale.eta_date.month]} {sale.eta_date.year}", 'product_name': product_name, 'product_not_in_id': product_not_in_id, 'details': [request.env['sale.order.line'].api_single_response(x, context='with_detail') for x in sale.order_line] @@ -257,6 +259,25 @@ class SaleOrder(controller.Controller): bulan = bulan_id[sale_order.expected_ready_to_ship.month - 1] tahun = sale_order.expected_ready_to_ship.year data['expected_ready_to_ship'] = f"{tanggal} {bulan} {tahun}" + if sale_order.eta_date_start: + bulan_id = [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", + "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ] + tanggal = sale_order.eta_date_start.day + bulan = bulan_id[sale_order.eta_date_start.month - 1] + tahun = sale_order.eta_date_start.year + data['eta_date_start'] = f"{tanggal} {bulan} {tahun}" + + if sale_order.eta_date: + bulan_id = [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", + "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ] + tanggal = sale_order.eta_date.day + bulan = bulan_id[sale_order.eta_date.month - 1] + tahun = sale_order.eta_date.year + data['eta_date_end'] = f"{tanggal} {bulan} {tahun}" return self.response(data) diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py index 0561043b..9be03927 100644 --- a/indoteknik_api/models/sale_order.py +++ b/indoteknik_api/models/sale_order.py @@ -29,6 +29,9 @@ class SaleOrder(models.Model): 'approval_step': APPROVAL_STEP[sale_order.web_approval] if sale_order.web_approval else 0, 'date_order': self.env['rest.api'].datetime_to_str(sale_order.date_order, '%d/%m/%Y %H:%M:%S'), 'payment_type': sale_order.payment_type, + 'carrier_id': sale_order.carrier_id.id, + 'carrier_name': sale_order.carrier_id.name, + 'service_type': sale_order.shipping_option_id.name, 'pickings': [] } for picking in sale_order.picking_ids: @@ -47,6 +50,13 @@ class SaleOrder(models.Model): 'eta' : response['eta'], 'id': picking.id, 'name': picking.name, + 'products': [{ + 'id': product.id, + 'name': product.name, + 'image': self.env['ir.attachment'].api_image('product.template', 'image_128', product.product_tmpl_id.id), + 'code': product.default_code or '' + } for product in picking.move_line_ids.product_id], + 'product_count': len(picking.move_line_ids) # 'tracking_number': picking.delivery_tracking_no or '', # 'delivered': picking.waybill_id.delivered or picking.driver_arrival_date != False or picking.sj_return_date != False, }) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f2f5f52a..9b124bd4 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -19,10 +19,10 @@ import re _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" +# biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" -# biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" class StockPicking(models.Model): @@ -1707,7 +1707,9 @@ class StockPicking(models.Model): if move_line.qty_done > 0: product_shipped.append({ 'name': move_line.product_id.name, - 'qty': move_line.qty_done + 'code': move_line.product_id.default_code, + 'qty': move_line.qty_done, + 'image': self.env['ir.attachment'].api_image('product.template', 'image_128', move_line.product_id.product_tmpl_id.id), }) response = { -- cgit v1.2.3 From d896d5cb51437d366c10e854616faee46736688c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 12 Aug 2025 15:10:16 +0700 Subject: get bill based on product --- indoteknik_custom/models/tukar_guling.py | 37 ++++++++++++++++------ indoteknik_custom/models/tukar_guling_po.py | 49 ++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 6aedb70e..ff641f34 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -92,19 +92,38 @@ class TukarGuling(models.Model): so = self.env['sale.order'].search([('name', '=', origin_str)], limit=1) rec.origin_so = so.id if so else False - @api.depends('origin') + @api.depends('origin', 'origin_so', 'partner_id', 'line_ids.product_id') def _compute_is_has_invoice(self): + Move = self.env['account.move'] for rec in self: - invoices = self.env['account.move'].search([ - ('invoice_origin', 'ilike', rec.origin), - ('move_type', '=', 'out_invoice'), # hanya invoice - ('state', 'not in', ['draft', 'cancel']) - ]) + rec.is_has_invoice = False + rec.invoice_id = [(5, 0, 0)] + + product_ids = rec.line_ids.mapped('product_id').ids + if not product_ids: + continue + + domain = [ + ('move_type', 'in', ['out_invoice', 'in_invoice']), + ('state', 'not in', ['draft', 'cancel']), + ('invoice_line_ids.product_id', 'in', product_ids), + ] + + if rec.partner_id: + domain.append(('partner_id', '=', rec.partner_id.id)) + + extra = [] + if rec.origin: + extra.append(('invoice_origin', 'ilike', rec.origin)) + if rec.origin_so: + extra.append(('invoice_line_ids.sale_line_ids.order_id', '=', rec.origin_so.id)) + if extra: + domain = domain + ['|'] * (len(extra) - 1) + extra + + invoices = Move.search(domain).with_context(active_test=False) if invoices: + rec.invoice_id = [(6, 0, invoices.ids)] rec.is_has_invoice = True - rec.invoice_id = invoices - else: - rec.is_has_invoice = False def set_opt(self): if not self.val_inv_opt and self.is_has_invoice == True: diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 03d7668f..cc1c79c0 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -73,19 +73,46 @@ class TukarGulingPO(models.Model): so = self.env['purchase.order'].search([('name', '=', origin_str)], limit=1) rec.origin_po = so.id if so else False - @api.depends('origin') + @api.depends('origin', 'origin_po', 'vendor_id', 'line_ids.product_id') def _compute_is_has_bill(self): + Move = self.env['account.move'] for rec in self: - bills = self.env['account.move'].search([ - ('invoice_origin', 'ilike', rec.origin), - ('move_type', '=', 'in_invoice'), # hanya vendor bill - ('state', 'not in', ['draft', 'cancel']) - ]) - if bills: - rec.is_has_bill = True - rec.bill_id = bills - else: - rec.is_has_bill = False + # reset + rec.is_has_bill = False + rec.bill_id = [(5, 0, 0)] + + product_ids = rec.line_ids.mapped('product_id').ids + if not product_ids: + continue + + # dasar: bill atau vendor credit note yang linennya mengandung produk TG + domain = [ + ('move_type', 'in', ['in_invoice', 'in_refund']), + ('state', 'not in', ['draft', 'cancel']), + ('invoice_line_ids.product_id', 'in', product_ids), + ] + + # batasi ke vendor sama (kalau ada) + if rec.vendor_id: + domain.append(('partner_id', '=', rec.vendor_id.id)) + + # bantu pembatasan ke asal dokumen + extra = [] + if rec.origin: + extra.append(('invoice_origin', 'ilike', rec.origin)) + if rec.origin_po: + # di Odoo 14, invoice line biasanya link ke purchase.line lewat purchase_line_id + extra.append(('invoice_line_ids.purchase_line_id.order_id', '=', rec.origin_po.id)) + + # OR-kan semua extra filter jika ada + if extra: + domain = domain + ['|'] * (len(extra) - 1) + extra + + bills = Move.search(domain).with_context(active_test=False) + + # --- Opsi 1: minimal salah satu produk TG muncul di bill (default) --- + rec.bill_id = [(6, 0, bills.ids)] + rec.is_has_bill = bool(bills) def set_opt(self): if not self.val_bil_opt and self.is_has_bill == True: -- cgit v1.2.3 From 974c0a3543820f7edef7c32cccba6a17367979b6 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 12 Aug 2025 15:55:28 +0700 Subject: Quotation download website --- indoteknik_custom/report/report_sale_order.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/report/report_sale_order.xml b/indoteknik_custom/report/report_sale_order.xml index b9928790..d1a6de28 100644 --- a/indoteknik_custom/report/report_sale_order.xml +++ b/indoteknik_custom/report/report_sale_order.xml @@ -96,8 +96,8 @@ - -
Invoice NumberTanggal InvoiceJatuh TempoSisa HariTotalPayment TermCustomer No. POReferensiInvoice NumberInvoice DateDue DateAmountTermDays To Due
{days_to_due}
{inv.partner_id.name}{inv.purchase_order_id.name or '-'}{inv.ref or '-'} {inv.name} {fields.Date.to_string(inv.invoice_date) or '-'} {fields.Date.to_string(inv.invoice_date_due) or '-'}Description Quantity Unit Price - Disc.% + + Disc.% Taxes @@ -125,7 +125,7 @@ + -- cgit v1.2.3 From 41ae119049bea955ca6edb5b5f63acd28b0eff91 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 13 Aug 2025 07:00:25 +0700 Subject: (andri) fix --- 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 b52df54c..a7dc59ba 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2418,7 +2418,7 @@ class SaleOrder(models.Model): order.approval_status = 'pengajuan1' return self._create_approval_notification('Sales Manager') elif order._requires_approval_team_sales(): - order.approval_status = 'approved' + # order.approval_status = 'approved' return self._create_approval_notification('Team Sales') order.approval_status = 'approved' -- cgit v1.2.3 From a2d087032e5b14901f4128c81b58143bcff4286f Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 13 Aug 2025 09:16:30 +0700 Subject: (andri) fix --- 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 a7dc59ba..0acfa0b0 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2188,7 +2188,7 @@ class SaleOrder(models.Model): self.check_product_bom() self.check_credit_limit() self.check_limit_so_to_invoice() - # order.approval_status = 'approved' + order.approval_status = 'pengajuan1' return self._create_approval_notification('Team Sales') raise UserError("Bisa langsung Confirm") @@ -2418,7 +2418,7 @@ class SaleOrder(models.Model): order.approval_status = 'pengajuan1' return self._create_approval_notification('Sales Manager') elif order._requires_approval_team_sales(): - # order.approval_status = 'approved' + order.approval_status = 'pengajuan1' return self._create_approval_notification('Team Sales') order.approval_status = 'approved' -- cgit v1.2.3