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 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 abbf20da12e9f9cdcb24b9018eecc6d52812f92e Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 8 Aug 2025 16:03:46 +0700 Subject: (andri) add email salesperson inv di CC --- indoteknik_custom/models/account_move.py | 66 +++++++++++++++------- .../views/mail_template_invoice_reminder.xml | 2 +- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 12978a2f..fd08ed60 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -144,17 +144,24 @@ class AccountMove(models.Model): today + timedelta(days=7), ] - partner = self.env['res.partner'].search([('name', 'ilike', 'DIRGANTARA YUDHA ARTHA')], limit=1) - if not partner: - _logger.info("Partner tidak ditemukan.") - return + # --- TESTING --- + # partner = self.env['res.partner'].search([('name', 'ilike', 'DIRGANTARA YUDHA ARTHA')], 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), + # ]) 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}") @@ -164,10 +171,9 @@ class AccountMove(models.Model): _logger.info(f"Invoices tahap 2: {invoices}") if not invoices: - _logger.info(f"Tidak ada invoice yang due untuk partner: {partner.name}") + _logger.info("Tidak ada invoice yang due") return - # Kelompokkan invoice berdasarkan partner & days_to_due invoice_group = {} for inv in invoices: dtd = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 @@ -176,13 +182,12 @@ class AccountMove(models.Model): template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') for (partner, dtd), invs in invoice_group.items(): - # Cari semua kontak anak yang reminder_invoices = True dan punya email + # Ambil child contact yang di-checklist reminder_invoices reminder_contacts = self.env['res.partner'].search([ ('parent_id', '=', partner.id), ('reminder_invoices', '=', True), ('email', '!=', False), ]) - _logger.info(f"Email Reminder Child {reminder_contacts}") if not reminder_contacts: @@ -197,7 +202,6 @@ class AccountMove(models.Model): email_to = ",".join(emails) _logger.info(f"Email tujuan: {email_to}") - # 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 @@ -213,12 +217,14 @@ class AccountMove(models.Model):
""" - 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
Invoice NumberTanggal InvoiceJatuh TempoSisa HariTotalPayment TermCustomer No. POReferensiInvoice NumberInvoice DateDue DateAmountTermDays To Due
{days_to_due}