diff options
| author | IT Fixcomart <it@fixcomart.co.id> | 2025-09-17 11:27:47 +0000 |
|---|---|---|
| committer | IT Fixcomart <it@fixcomart.co.id> | 2025-09-17 11:27:47 +0000 |
| commit | 1fdeef8073eb35b407bb0b3cdb26bf635b3b1629 (patch) | |
| tree | 9c423f561f8929bdeee06ed707d2e08da9c3f246 /indoteknik_custom/models | |
| parent | ef9daab07049de822b7137b4a9a5d3f1fba53992 (diff) | |
| parent | 886c28f6ebf20dcca5252341a8f6b61cd4d89d71 (diff) | |
Merged in form-sp (pull request #423)
Form sp
Diffstat (limited to 'indoteknik_custom/models')
| -rwxr-xr-x | indoteknik_custom/models/__init__.py | 4 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_move.py | 15 | ||||
| -rw-r--r-- | indoteknik_custom/models/letter_receivable.py | 496 | ||||
| -rw-r--r-- | indoteknik_custom/models/unpaid_invoice_view.py | 55 |
4 files changed, 561 insertions, 9 deletions
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index c0aa7085..6dc61277 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -157,4 +157,6 @@ from . import refund_sale_order from . import tukar_guling from . import tukar_guling_po from . import update_date_planned_po_wizard -from . import sj_tele
\ No newline at end of file +from . import unpaid_invoice_view +from . import letter_receivable +from . import sj_tele diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index f10ca23f..ec23c626 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -218,8 +218,7 @@ class AccountMove(models.Model): ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), ('invoice_date_due', '<', today), ('date_terima_tukar_faktur', '!=', False), - ('invoice_payment_term_id.name', 'ilike', 'tempo'), - ('partner_id', 'in', [94603])]) + ('invoice_payment_term_id.name', 'ilike', 'tempo')]) _logger.info(f"Found {len(invoices)} invoices overdue for reminder {invoices}.") if not invoices: _logger.info("Tidak ada invoice yang overdue") @@ -423,20 +422,20 @@ class AccountMove(models.Model): # Siapkan email values values = { - 'subject': f"Reminder Invoice Due Test - {partner.name}", + 'subject': f"Reminder Invoice Due - {partner.name}", 'email_to': 'andrifebriyadiputra@gmail.com', - # 'email_to': email_to, + 'email_to': email_to, 'email_from': 'finance@indoteknik.co.id', - # 'email_cc': ",".join(sorted(set(cc_list))), + 'email_cc': ",".join(sorted(set(cc_list))), 'body_html': body_html, - # 'reply_to': 'finance@indoteknik.co.id', + 'reply_to': 'finance@indoteknik.co.id', } template.send_mail(invs[0].id, force_send=True, email_values=values) - # _logger.info(f"Mengirim email ke: {values['email_to']} > email CC: {values['email_cc']}") + _logger.info(f"Mengirim email ke: {values['email_to']} > email CC: {values['email_cc']}") _logger.info(f"Reminder terkirim ke {partner.name} ({values['email_to']}) → {len(invs)} invoice (dtd = {dtd})") # flag - # invs.write({'reminder_sent_date': today}) + 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 diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py new file mode 100644 index 00000000..1445800f --- /dev/null +++ b/indoteknik_custom/models/letter_receivable.py @@ -0,0 +1,496 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import UserError +from odoo.exceptions import ValidationError +from odoo.tools import mail, formatLang +from terbilang import Terbilang +import re +import logging +from datetime import datetime, timedelta +import babel +import base64 +import pytz + +_logger = logging.getLogger(__name__) + +class SuratPiutang(models.Model): + _name = "surat.piutang" + _description = "Surat Piutang" + _inherit = ['mail.thread', 'mail.activity.mixin'] + _order = 'name desc' + + name = fields.Char(string="Nomor Surat", readonly=True, copy=False) + partner_id = fields.Many2one("res.partner", string="Customer", required=True, tracking=True) + tujuan_nama = fields.Char(string="Nama Tujuan", tracking=True) + tujuan_email = fields.Char(string="Email Tujuan", tracking=True) + perihal = fields.Selection([ + ('penagihan', 'Surat Resmi Penagihan'), + ('sp1', 'Surat Peringatan Piutang ke-1'), + ('sp2', 'Surat Peringatan Piutang ke-2'), + ('sp3', 'Surat Peringatan Piutang ke-3') + ], string="Perihal", required=True, tracking=True) + line_ids = fields.One2many("surat.piutang.line", "surat_id", string="Invoice Lines") + state = fields.Selection([ + ("draft", "Draft"), + ("waiting_approval_sales", "Menunggu Approval Sales Manager"), + ("waiting_approval_pimpinan", "Menunggu Approval Pimpinan / Kirim Surat"), + ("sent", "Approved & Sent") + ], default="draft", tracking=True) + send_date = fields.Datetime(string="Tanggal Kirim", tracking=True) + seven_days_after_sent_date = fields.Char(string="7 Hari Setelah Tanggal Kirim") + periode_invoices_terpilih = fields.Char( + string="Periode Invoices Terpilih", + compute="_compute_periode_invoices", + ) + + currency_id = fields.Many2one('res.currency') + + # Grand total (total sisa semua line yang dicentang) + grand_total = fields.Monetary( + string='Total Sisa', + currency_field='currency_id', + compute='_compute_grand_total', + ) + + grand_total_text = fields.Char( + string="Total Terbilang", + compute="_compute_grand_total_text", + ) + + perihal_label = fields.Char(compute="_compute_perihal_label", string="Perihal Label") + + payment_difficulty = fields.Selection(string="Payment Difficulty", related='partner_id.payment_difficulty', readonly=True) + + sales_person_id = fields.Many2one('res.users', string='Salesperson', related='partner_id.user_id', readonly=True) + + PERIHAL_SEQUENCE = { + "penagihan": "sp1", + "sp1": "sp2", + "sp2": "sp3", + } + + @api.onchange('partner_id') + def _onchange_partner_id_domain(self): + unpaid_partner_ids = self.env['unpaid.invoice.view'].search([]).mapped('partner_id.id') + return { + 'domain': { + 'partner_id': [('id', 'in', unpaid_partner_ids)] + } + } + def _compute_perihal_label(self): + for rec in self: + rec.perihal_label = dict(self._fields['perihal'].selection).get(rec.perihal, '') + + def action_create_next_letter(self): + for rec in self: + if rec.state != "sent": + raise UserError("Surat harus sudah terkirim sebelum bisa membuat surat lanjutan.") + + next_perihal = self.PERIHAL_SEQUENCE.get(rec.perihal) + if not next_perihal: + raise UserError("Surat ini sudah pada tahap terakhir (SP3). Tidak bisa membuat lanjutan lagi.") + + existing = self.search([ + ('partner_id', '=', rec.partner_id.id), + ('perihal', '=', next_perihal), + ('state', '!=', 'draft') # optional: cek hanya yang sudah dikirim + ]) + if existing: + raise UserError(f"Surat lanjutan {dict(self._fields['perihal'].selection).get(next_perihal)} " + f"untuk customer ini sudah dibuat: {', '.join(existing.mapped('name'))}") + + # copy surat lama + new_vals = { + "tujuan_nama": rec.tujuan_nama, + "tujuan_email": rec.tujuan_email, + "perihal": next_perihal, + "partner_id": rec.partner_id.id, + "line_ids": [(0, 0, { + 'invoice_id': line.invoice_id.id, + 'invoice_number': line.invoice_number, + 'invoice_date': line.invoice_date, + 'invoice_date_due': line.invoice_date_due, + 'invoice_day_to_due': line.invoice_day_to_due, + 'new_invoice_day_to_due': line.new_invoice_day_to_due, + 'ref': line.ref, + 'amount_residual': line.amount_residual, + 'currency_id': line.currency_id.id, + 'payment_term_id': line.payment_term_id.id, + 'date_kirim_tukar_faktur': line.date_kirim_tukar_faktur, + 'date_terima_tukar_faktur': line.date_terima_tukar_faktur, + 'invoice_user_id': line.invoice_user_id.id, + 'sale_id': line.sale_id.id, + "selected": line.selected, + }) for line in rec.line_ids], + } + new_letter = self.create(new_vals) + self.env.user.notify_info( + message=f"{dict(self._fields['perihal'].selection).get(next_perihal)} berhasil dibuat ({new_letter.name}).", + title="Informasi", + sticky=False + ) + new_letter.message_post( + body= + f"<b>{dict(self._fields['perihal'].selection).get(next_perihal)}</b> " + f"berhasil dibuat berdasarkan surat sebelumnya.<br/>" + f"Nomor Surat: <b>{new_letter.name}</b>" + ) + rec.message_post( + body=( + f"Surat lanjutan dengan perihal <b>{dict(self._fields['perihal'].selection).get(next_perihal)}</b> " + f"telah dibuat sebagai kelanjutan dari surat ini.<br/>" + f"Nomor Surat Baru: <a href='/web#id={new_letter.id}&model=surat.piutang&view_type=form'><b>{new_letter.name}</b></a>" + ) + ) + return True + + + @api.depends("line_ids.selected", "line_ids.invoice_date") + def _compute_periode_invoices(self): + for rec in self: + selected_lines = rec.line_ids.filtered(lambda l: l.selected and l.invoice_date) + if not selected_lines: + rec.periode_invoices_terpilih = "-" + continue + + dates = selected_lines.mapped("invoice_date") + min_date, max_date = min(dates), max(dates) + + # Ambil bagian bulan & tahun + min_month = babel.dates.format_date(min_date, "MMMM", locale="id_ID") + min_year = min_date.year + max_month = babel.dates.format_date(max_date, "MMMM", locale="id_ID") + max_year = max_date.year + + if min_year == max_year: + if min_month == max_month: + # example: Januari 2025 + rec.periode_invoices_terpilih = f"{min_month} {min_year}" + else: + # example: Mei s/d Juni 2025 + rec.periode_invoices_terpilih = f"{min_month} s/d {max_month} {max_year}" + else: + # example: Desember 2024 s/d Januari 2025 + rec.periode_invoices_terpilih = f"{min_month} {min_year} s/d {max_month} {max_year}" + + def _compute_grand_total_text(self): + tb = Terbilang() + for record in self: + res = "" + if record.grand_total and record.grand_total > 0: + try: + tb.parse(int(record.grand_total)) + res = tb.getresult().title() + " Rupiah" + except Exception: + res = "" + record.grand_total_text = res + + @api.depends('line_ids.amount_residual', 'line_ids.selected') + def _compute_grand_total(self): + for rec in self: + rec.grand_total = sum( + line.amount_residual or 0.0 for line in rec.line_ids if line.selected + ) + + @api.constrains("tujuan_email") + def _check_email_format(self): + for rec in self: + if rec.tujuan_email and not mail.single_email_re.match(rec.tujuan_email): + raise ValidationError(_("Format email tidak valid: %s") % rec.tujuan_email) + + def action_approve(self): + wib = pytz.timezone('Asia/Jakarta') + now_wib = datetime.now(wib) + + sales_manager_ids = [10] # ganti dengan ID user Sales Manager + pimpinan_user_ids = [7] # ganti dengan ID user Pimpinan + + for rec in self: + # === SP1 s/d SP3 butuh dua tahap approval === + if rec.perihal in ("sp1", "sp2", "sp3"): + + # Tahap 1: Sales Manager approval + if rec.state == "waiting_approval_sales": + if self.env.user.id not in sales_manager_ids: + raise UserError("Hanya Sales Manager yang boleh menyetujui tahap ini.") + rec.state = "waiting_approval_pimpinan" + rec.message_post(body="Disetujui oleh Sales Manager. Menunggu Approval Pimpinan.") + continue + + # Tahap 2: Pimpinan approval + if rec.state == "waiting_approval_pimpinan": + if self.env.user.id not in pimpinan_user_ids: + raise UserError("Hanya Pimpinan yang berhak menyetujui surat ini.") + rec.state = "sent" + now_utc = now_wib.astimezone(pytz.UTC).replace(tzinfo=None) + rec.send_date = now_utc + rec.action_send_letter() + rec.message_post(body="Surat Piutang disetujui oleh Pimpinan dan berhasil dikirim.") + continue + + # === Surat penagihan biasa (langsung Pimpinan approve) === + if rec.perihal == "penagihan": + # if self.env.user.id not in pimpinan_user_ids: + # raise UserError("Hanya Pimpinan yang boleh menyetujui surat penagihan.") + rec.state = "sent" + now_utc = now_wib.astimezone(pytz.UTC).replace(tzinfo=None) + rec.send_date = now_utc + rec.action_send_letter() + rec.message_post(body="Surat Penagihan disetujui dan berhasil dikirim.") + + self.env.user.notify_info( + message=f"Surat piutang {rec.name} berhasil dikirim ke {rec.partner_id.name} ({rec.tujuan_email})", + title="Informasi", + sticky=False + ) + + def action_send_letter(self): + self.ensure_one() + + selected_lines = self.line_ids.filtered('selected') + if not selected_lines: + raise UserError(_("Tidak ada invoice yang dicentang untuk dikirim.")) + + if not self.tujuan_email: + raise UserError(_("Email tujuan harus diisi.")) + + template = self.env.ref('indoteknik_custom.letter_receivable_mail_template') + # today = fields.Date.today() + + month_map = { + 1: "Januari", 2: "Februari", 3: "Maret", 4: "April", + 5: "Mei", 6: "Juni", 7: "Juli", 8: "Agustus", + 9: "September", 10: "Oktober", 11: "November", 12: "Desember", + } + target_date = (self.send_date or fields.Datetime.now()).date() + timedelta(days=7) + self.seven_days_after_sent_date = f"{target_date.day} {month_map[target_date.month]}" + + perihal_map = { + 'penagihan': 'Surat Resmi Penagihan', + 'sp1': 'Surat Peringatan Pertama (I)', + 'sp2': 'Surat Peringatan Kedua (II)', + 'sp3': 'Surat Peringatan Ketiga (III)', + } + perihal_text = perihal_map.get(self.perihal, self.perihal or '') + + invoice_table_rows = "" + grand_total = 0 + for line in selected_lines: + # days_to_due = (line.invoice_date_due - today).days if line.invoice_date_due else 0 + grand_total += line.amount_residual + invoice_table_rows += f""" + <tr> + <td>{line.invoice_number or '-'}</td> + <td>{self.partner_id.name or '-'}</td> + <td>{fields.Date.to_string(line.invoice_date) or '-'}</td> + <td>{fields.Date.to_string(line.invoice_date_due) or '-'}</td> + <td>{line.new_invoice_day_to_due}</td> + <td>{line.ref or '-'}</td> + <td>{formatLang(self.env, line.amount_residual, currency_obj=line.currency_id)}</td> + <td>{line.payment_term_id.name or '-'}</td> + </tr> + """ + + invoice_table_footer = f""" + <tfoot> + <tr style="font-weight:bold; background-color:#f9f9f9;"> + <td colspan="6" align="right">Grand Total</td> + <td>{formatLang(self.env, grand_total, currency_obj=self.currency_id, monetary=True)}</td> + <td colspan="2"></td> + </tr> + </tfoot> + """ + # inject table rows ke template + body_html = re.sub( + r"<tbody[^>]*>.*?</tbody>", + f"<tbody>{invoice_table_rows}</tbody>{invoice_table_footer}", + template.body_html, + flags=re.DOTALL + ).replace('${object.name}', self.name or '') \ + .replace('${object.partner_id.name}', self.partner_id.name or '') \ + .replace('${object.seven_days_after_sent_date}', self.seven_days_after_sent_date or '') \ + .replace('${object.perihal}', perihal_text or '') + + report = self.env.ref('indoteknik_custom.action_report_surat_piutang') + pdf_content, _ = report._render_qweb_pdf([self.id]) + attachment_base64 = base64.b64encode(pdf_content) + + attachment = self.env['ir.attachment'].create({ + 'name': f"{self.perihal_label} - {self.partner_id.name}.pdf", + 'type': 'binary', + 'datas': attachment_base64, + 'res_model': 'surat.piutang', + 'res_id': self.id, + 'mimetype': 'application/pdf', + }) + + values = { + # 'subject': template.subject.replace('${object.name}', self.name or ''), + 'subject': perihal_map.get(self.perihal, self.perihal or '') + " - " + (self.partner_id.name or ''), + 'email_to': self.tujuan_email, + 'email_from': 'finance@indoteknik.co.id', + 'body_html': body_html, + 'attachments': [(attachment.name, attachment.datas)], + 'reply_to': 'finance@indoteknik.co.id', + } + + template.with_context(mail_post_autofollow=False).send_mail( + self.id, + force_send=True, + email_values=values + ) + + _logger.info( + f"Surat Piutang {self.name} terkirim ke {self.tujuan_email} " + f"({self.partner_id.name}), total {len(selected_lines)} invoice." + ) + + @api.onchange('partner_id') + def _onchange_partner_id(self): + if self.partner_id: + invoice_lines = self.env['unpaid.invoice.view'].search( + [('partner_id', '=', self.partner_id.id)], + order='new_invoice_day_to_due asc' + ) + selected_invoice_id = self.env.context.get('default_selected_invoice_id') + lines = [(5, 0, 0)] # hapus semua line lama + lines += [(0, 0, { + 'invoice_id': inv.invoice_id.id, + 'invoice_number': inv.invoice_number, + 'invoice_date': inv.invoice_date, + 'invoice_date_due': inv.invoice_date_due, + 'invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.new_invoice_day_to_due, + 'ref': inv.ref, + 'amount_residual': inv.amount_residual, + 'currency_id': inv.currency_id.id, + 'payment_term_id': inv.payment_term_id.id, + 'date_kirim_tukar_faktur': inv.date_kirim_tukar_faktur, + 'date_terima_tukar_faktur': inv.date_terima_tukar_faktur, + 'invoice_user_id': inv.invoice_user_id.id, + 'sale_id': inv.sale_id.id, + 'selected': True if inv.invoice_id.id == selected_invoice_id else False, + }) for inv in invoice_lines] + self.line_ids = lines + + def action_refresh_lines(self): + for rec in self: + if not rec.partner_id: + continue + + # Ambil semua unpaid terbaru + invoice_views = self.env['unpaid.invoice.view'].search( + [('partner_id', '=', rec.partner_id.id)], + order='new_invoice_day_to_due asc' + ) + + existing_lines = {line.invoice_id.id: line for line in rec.line_ids} + + # Cache selected status per invoice id + selected_map = {line.invoice_id.id: line.selected for line in rec.line_ids} + + # Invoice id yang masih ada di unpaid + new_invoice_ids = invoice_views.mapped('invoice_id.id') + + for inv in invoice_views: + if inv.invoice_id.id in existing_lines: + # update line lama + line = existing_lines[inv.invoice_id.id] + line.write({ + # 'invoice_view_id': inv.id, + 'invoice_number': inv.invoice_number, + 'invoice_date': inv.invoice_date, + 'invoice_date_due': inv.invoice_date_due, + 'invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.new_invoice_day_to_due, + 'ref': inv.ref, + 'amount_residual': inv.amount_residual, + 'currency_id': inv.currency_id.id, + 'payment_term_id': inv.payment_term_id.id, + 'date_kirim_tukar_faktur': inv.date_kirim_tukar_faktur, + 'date_terima_tukar_faktur': inv.date_terima_tukar_faktur, + 'invoice_user_id': inv.invoice_user_id.id, + 'sale_id': inv.sale_id.id, + 'selected': selected_map.get(inv.invoice_id.id, line.selected), + }) + else: + # preserve selected kalau pernah ada di cache + self.env['surat.piutang.line'].create({ + 'surat_id': rec.id, + # 'invoice_view_id': inv.id, + 'invoice_id': inv.invoice_id.id, + 'invoice_number': inv.invoice_number, + 'invoice_date': inv.invoice_date, + 'invoice_date_due': inv.invoice_date_due, + 'invoice_day_to_due': inv.invoice_day_to_due, + 'new_invoice_day_to_due': inv.new_invoice_day_to_due, + 'ref': inv.ref, + 'amount_residual': inv.amount_residual, + 'currency_id': inv.currency_id.id, + 'payment_term_id': inv.payment_term_id.id, + 'date_kirim_tukar_faktur': inv.date_kirim_tukar_faktur, + 'date_terima_tukar_faktur': inv.date_terima_tukar_faktur, + 'invoice_user_id': inv.invoice_user_id.id, + 'sale_id': inv.sale_id.id, + 'selected': selected_map.get(inv.invoice_id.id, False), + }) + + # Hapus line yang tidak ada lagi di unpaid view + rec.line_ids.filtered(lambda l: l.invoice_id.id not in new_invoice_ids).unlink() + + rec.message_post( + body=f"Line Invoices diperbarui. Total line saat ini: {len(rec.line_ids)}" + ) + + @api.model + def create(self, vals): + # Generate nomor surat otomatis + if not vals.get("name"): + seq = self.env["ir.sequence"].next_by_code("surat.piutang") or "000" + today = fields.Date.today() + bulan_romawi = ["I","II","III","IV","V","VI","VII","VIII","IX","X","XI","XII"][today.month-1] + tahun = today.strftime("%y") + vals["name"] = f"{seq}/LO/FAT/IDG/{bulan_romawi}/{tahun}" + if vals.get("perihal") == "penagihan": + vals["state"] = "waiting_approval_pimpinan" + else: + vals["state"] = "waiting_approval_sales" + return super().create(vals) + +class SuratPiutangLine(models.Model): + _name = 'surat.piutang.line' + _description = 'Surat Piutang Line' + + surat_id = fields.Many2one('surat.piutang', string='Surat Piutang', ondelete='cascade') + # invoice_view_id = fields.Many2one('unpaid.invoice.view', string='Unpaid Invoice') + invoice_id = fields.Many2one('account.move', string='Invoice') + selected = fields.Boolean(string="Pilih", default=False) + + invoice_number = fields.Char(string='Invoice Number') + invoice_date = fields.Date(string='Invoice Date') + invoice_date_due = fields.Date(string='Due Date') + invoice_day_to_due = fields.Integer(string='Day to Due') + new_invoice_day_to_due = fields.Integer(string='New Day to Due') + ref = fields.Char(string='Reference') + amount_residual = fields.Monetary(string='Amount Due Signed') + currency_id = fields.Many2one('res.currency') + payment_term_id = fields.Many2one('account.payment.term', string='Payment Terms') + + date_kirim_tukar_faktur = fields.Date(string='Kirim Faktur') + date_terima_tukar_faktur = fields.Date(string='Terima Faktur') + invoice_user_id = fields.Many2one('res.users', string='Salesperson') + sale_id = fields.Many2one('sale.order', string='Sale Order') + + sort = fields.Integer(string='No Urut', compute='_compute_sort', store=False) + + @api.depends('surat_id.line_ids.selected') + def _compute_sort(self): + for line in self: + if line.surat_id: + # Ambil semua line yang selected + selected_lines = line.surat_id.line_ids.filtered(lambda l: l.selected) + try: + line.sort = selected_lines.ids.index(line.id) + 1 + except ValueError: + line.sort = 0 + else: + line.sort = 0 diff --git a/indoteknik_custom/models/unpaid_invoice_view.py b/indoteknik_custom/models/unpaid_invoice_view.py new file mode 100644 index 00000000..3eb6efc7 --- /dev/null +++ b/indoteknik_custom/models/unpaid_invoice_view.py @@ -0,0 +1,55 @@ +from odoo import models, fields + +class UnpaidInvoiceView(models.Model): + _name = 'unpaid.invoice.view' + _description = 'Unpaid Invoices Monitoring' + _auto = False + _rec_name = 'partner_name' + _order = 'partner_name, new_invoice_day_to_due DESC' + + partner_id = fields.Many2one('res.partner', string='Partner') + partner_name = fields.Char(string='Partner Name') + # email = fields.Char() + # phone = fields.Char() + invoice_id = fields.Many2one('account.move', string='Invoice') + invoice_number = fields.Char(string='Invoice Number') + invoice_date = fields.Date() + invoice_date_due = fields.Date(string='Due Date') + date_terima_tukar_faktur = fields.Date(string='Terima Faktur') + currency_id = fields.Many2one('res.currency', string='Currency') + amount_total = fields.Monetary(string='Total Amount', currency_field='currency_id') + amount_residual = fields.Monetary(string='Sisa Amount', currency_field='currency_id') + payment_state = fields.Selection([ + ('not_paid', 'Not Paid'), + ('in_payment', 'In Payment'), + ('paid', 'Paid'), + ('partial', 'Partially Paid'), + ('reversed', 'Reversed')], string='Payment State') + payment_term_id = fields.Many2one('account.payment.term', string='Payment Term') + invoice_day_to_due = fields.Integer(string="Day to Due") + new_invoice_day_to_due = fields.Integer(string="New Day Due") + + ref = fields.Char(string='Reference') + invoice_user_id = fields.Many2one('res.users', string='Salesperson') + date_kirim_tukar_faktur = fields.Date(string='Kirim Faktur') + sale_id = fields.Many2one('sale.order', string='Sale Order') + + payment_difficulty = fields.Selection([ + ('bermasalah', 'Bermasalah'), + ('sulit', 'Sulit'), + ('agak_sulit', 'Agak Sulit'), + ('normal', 'Normal'), + ], string="Payment Difficulty") + + def action_create_surat_piutang(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'res_model': 'surat.piutang', + 'view_mode': 'form', + 'target': 'current', + 'context': { + 'default_partner_id': self.partner_id.id, + 'default_selected_invoice_id': self.invoice_id.id, + } + } |
