summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFIN-IT_AndriFP <it@fixcomart.co.id>2025-09-25 09:04:32 +0700
committerFIN-IT_AndriFP <it@fixcomart.co.id>2025-09-25 09:04:32 +0700
commit65c0ccd8b6befa65e912e9a0126cf2ef8bdd78d7 (patch)
tree3ce6d9106c644f84f2a908f1642846a689075fe1
parent8b179c22149366ecf4dd9de6e5e7b5be612e355b (diff)
parentcd0592f7b7248d25d1b7de728af87117ae0b5876 (diff)
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into closing-apt
-rw-r--r--indoteknik_api/controllers/api_v1/partner.py11
-rw-r--r--indoteknik_api/models/res_users.py9
-rw-r--r--indoteknik_custom/models/account_move.py6
-rw-r--r--indoteknik_custom/models/letter_receivable.py12
-rwxr-xr-xindoteknik_custom/models/purchase_order.py11
-rw-r--r--indoteknik_custom/models/refund_sale_order.py42
-rw-r--r--indoteknik_custom/models/res_partner.py5
-rwxr-xr-xindoteknik_custom/models/sale_order.py80
-rw-r--r--indoteknik_custom/models/stock_picking.py13
-rw-r--r--indoteknik_custom/models/stock_picking_return.py2
-rw-r--r--indoteknik_custom/views/account_move.xml9
-rw-r--r--indoteknik_custom/views/account_move_line.xml3
-rw-r--r--indoteknik_custom/views/refund_sale_order.xml1
-rw-r--r--indoteknik_custom/views/res_partner.xml1
-rw-r--r--indoteknik_custom/views/unpaid_invoice_view.xml12
15 files changed, 195 insertions, 22 deletions
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py
index b1d8d5f3..8d67800c 100644
--- a/indoteknik_api/controllers/api_v1/partner.py
+++ b/indoteknik_api/controllers/api_v1/partner.py
@@ -295,7 +295,14 @@ class Partner(controller.Controller):
partner = partner.parent_id or partner
- if any(line.days == 0 for line in partner.property_payment_term_id.line_ids):
+ payment_term = (
+ partner.previous_payment_term_id
+ if partner.is_cbd_locked
+ else partner.property_payment_term_id
+ )
+
+ # if any(line.days == 0 for line in partner.property_payment_term_id.line_ids):
+ if any(line.days == 0 for line in payment_term.line_ids):
return self.response(code=402, description='Partner not tempo')
domain_result_tempo = [('partner_id', '=', partner.id), ('payment_state', '=', 'not_paid'), ('state', '=', 'posted')]
@@ -315,7 +322,7 @@ class Partner(controller.Controller):
data = {
'name': partner.name,
- 'payment_term': partner.property_payment_term_id.name,
+ 'payment_term': payment_term.name,
'amount_due': result_tempo,
'amount_due_total': result_tempo_total,
'amount_jatuh_tempo_total': result_jatuh_tempo_total,
diff --git a/indoteknik_api/models/res_users.py b/indoteknik_api/models/res_users.py
index 015b9e0e..c4e19bf3 100644
--- a/indoteknik_api/models/res_users.py
+++ b/indoteknik_api/models/res_users.py
@@ -14,6 +14,11 @@ class ResUsers(models.Model):
'manager': 2,
'director': 3
}
+ payment_term = (
+ main_partner.previous_payment_term_id
+ if main_partner.is_cbd_locked
+ else main_partner.property_payment_term_id
+ )
partner_tempo = False
is_tempo_request = request.env['user.pengajuan.tempo.request'].search([('user_company_id', '=', main_partner.id)], limit=1)
tempo_progres = (
@@ -21,8 +26,8 @@ class ResUsers(models.Model):
'rejected' if is_tempo_request.state_tempo == 'reject' else
'approve' if is_tempo_request.state_tempo == 'approval_director' else ''
)
- if main_partner:
- partner_tempo = True if 'tempo' in main_partner.get_check_payment_term().lower() else False
+ if payment_term:
+ partner_tempo = True if 'tempo' in payment_term.name.lower() else False
data = {
'id': res_user.id,
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py
index 96f791c5..44b3cb76 100644
--- a/indoteknik_custom/models/account_move.py
+++ b/indoteknik_custom/models/account_move.py
@@ -99,6 +99,8 @@ class AccountMove(models.Model):
reminder_sent_date = fields.Date(string="Tanggal Reminder Terkirim")
+ payment_difficulty = fields.Selection(string="Payment Difficulty", related='partner_id.payment_difficulty', readonly=True)
+
customer_promise_date = fields.Date(
string="Janji Bayar",
help="Tanggal janji bayar dari customer setelah reminder dikirim.",
@@ -333,12 +335,14 @@ class AccountMove(models.Model):
currency = invs[0].currency_id if invs else partner.company_id.currency_id
tempo_link = 'https://indoteknik.com/my/tempo'
# tempo_link = 'http://localhost:2100/my/tempo'
+ # payment_term = partner.previous_payment_term_id if partner.is_cbd_locked else partner.property_payment_term_id
+ payment_term = invs[0].invoice_payment_term_id
limit_info_html = f"""
<p><b>Informasi Tambahan:</b></p>
<ul style="font-size:12px; color:#333; line-height:1.4; margin:0; padding-left:0; list-style-position:inside;">
<li>Kredit Limit Anda: {formatLang(self.env, blocking_limit, currency_obj=currency)}</li>
- <li>Status Detail Tempo: {partner.property_payment_term_id.name or 'Review'}</li>
+ <li>Status Detail Tempo: {payment_term.name or ''}</li>
<li style="color:{'red' if (blocking_limit - outstanding_amount) < 0 else 'green'};">
Sisa Kredit Limit: {formatLang(self.env, blocking_limit - outstanding_amount, currency_obj=currency)}
</li>
diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py
index 1445800f..16034938 100644
--- a/indoteknik_custom/models/letter_receivable.py
+++ b/indoteknik_custom/models/letter_receivable.py
@@ -323,11 +323,23 @@ class SuratPiutang(models.Model):
'mimetype': 'application/pdf',
})
+ cc_list = [
+ 'finance@indoteknik.co.id',
+ 'akbar@indoteknik.co.id',
+ 'stephan@indoteknik.co.id',
+ 'darren@indoteknik.co.id'
+ ]
+
+ sales_email = self.sales_person_id.email if self.sales_person_id else None
+ if sales_email and sales_email not in cc_list:
+ cc_list.append(sales_email)
+
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',
+ 'email_cc': ",".join(sorted(set(cc_list))),
'body_html': body_html,
'attachments': [(attachment.name, attachment.datas)],
'reply_to': 'finance@indoteknik.co.id',
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 0304b5e2..b34ec926 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -1057,8 +1057,19 @@ class PurchaseOrder(models.Model):
message="Produk "+line.product_id.name+" memiliki vendor berbeda dengan SO (Vendor PO: "+str(self.partner_id.name)+", Vendor SO: "+str(line.so_line_id.vendor_id.name)+")",
sticky=True
)
+
+ def _check_assets_note(self):
+ for order in self:
+ # Cari apakah ada line dengan produk ID 614469 ('Assets Mesin & Peralatan')
+ asset_line = order.order_line.filtered(lambda l: l.product_id.id == 595346)
+ if asset_line and not order.notes:
+ raise UserError(_(
+ "%s berisi produk 'Assets Mesin & Peralatan'. "
+ "Harap isi Notes untuk menjelaskan kebutuhan dan divisi terkait."
+ ) % order.name)
def button_confirm(self):
+ self._check_assets_note()
# self._check_payment_term() # check payment term
res = super(PurchaseOrder, self).button_confirm()
current_time = datetime.now()
diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py
index 687acd6d..96082447 100644
--- a/indoteknik_custom/models/refund_sale_order.py
+++ b/indoteknik_custom/models/refund_sale_order.py
@@ -51,6 +51,7 @@ class RefundSaleOrder(models.Model):
account_no = fields.Char(string='Account No', required=True)
kcp = fields.Char(string='Alamat KCP')
finance_note = fields.Text(string='Finance Note')
+ biaya_admin = fields.Float(string='Biaya Admin Transfer')
invoice_names = fields.Html(string="Group Invoice Number", compute="_compute_invoice_names")
so_names = fields.Html(string="Group SO Number", compute="_compute_so_names")
@@ -280,8 +281,11 @@ class RefundSaleOrder(models.Model):
if refund_type == 'salah_transfer' and vals.get('transfer_move_id'):
move = self.env['account.move'].browse(vals['transfer_move_id'])
if move:
+ sisa_uang_masuk = move.amount_total_signed # ← set dengan nilai move
vals['uang_masuk'] = move.amount_total_signed
vals['remaining_refundable'] = 0
+ else:
+ sisa_uang_masuk = 0.0
else:
# ==== perhitungan normal ====
moves = self.env['account.move'].search([
@@ -902,6 +906,15 @@ class RefundSaleOrder(models.Model):
for rec in self:
if not is_fat:
raise UserError("Hanya Finance yang dapat mengkonfirmasi pembayaran refund.")
+ is_journal = self.env['account.move'].search([
+ ('refund_id', '=', rec.id),
+ ('state', '=', 'posted')
+ ])
+ amount = rec.amount_refund + rec.biaya_admin
+ if not is_journal:
+ raise UserError("Journal Payment Refund belum dibuat, buat Journal Payment Refund sebelum confirm refund.")
+ if is_journal and amount != sum(is_journal.mapped('amount_total_signed')):
+ raise UserError("Total Refund dengan Total Journal Harus Sama.")
if rec.status_payment == 'pending':
rec.status_payment = 'done'
rec.refund_date = fields.Date.context_today(self)
@@ -958,6 +971,7 @@ class RefundSaleOrder(models.Model):
# Ref format
ref_text = f"{refund_type_label} {refund.name or ''} {partner.display_name}".upper()
+ admintex = f"BIAYA ADMIN BANK {refund_type_label} {refund.name or ''} {partner.display_name}".upper()
# Buat Account Move (Journal Entry)
account_move = self.env['account.move'].create({
@@ -968,10 +982,10 @@ class RefundSaleOrder(models.Model):
'refund_so_ids': [(6, 0, refund.sale_order_ids.ids)],
'partner_id': partner.id,
})
-
+ admintf = refund.biaya_admin
amount = refund.amount_refund
# 450 Penerimaan Belum Teridentifikasi, 668 Penerimaan Belum Alokasi
- second_account_id = 450 if refund.refund_type not in ['barang_kosong', 'barang_kosong_sebagian'] else 668
+ second_account_id = 450 if refund.refund_type not in ['barang_kosong', 'barang_kosong_sebagian', 'barang_kosong_indent'] else 668
debit_line = {
'move_id': account_move.id,
@@ -983,6 +997,16 @@ class RefundSaleOrder(models.Model):
'name': ref_text,
}
+ adminline = {
+ 'move_id': account_move.id,
+ 'account_id': 555,
+ 'partner_id': partner.id,
+ 'currency_id': 12,
+ 'debit': admintf,
+ 'credit': 0.0,
+ 'name': admintex,
+ }
+
credit_line = {
'move_id': account_move.id,
'account_id': 389, # Intransit BCA
@@ -993,7 +1017,19 @@ class RefundSaleOrder(models.Model):
'name': ref_text,
}
- self.env['account.move.line'].create([debit_line, credit_line])
+ credit_admin_line = {
+ 'move_id': account_move.id,
+ 'account_id': 389, # Intransit BCA
+ 'partner_id': partner.id,
+ 'currency_id': 12,
+ 'debit': 0.0,
+ 'credit': admintf,
+ 'name': admintex,
+ }
+
+ journal_line = [debit_line, credit_line, adminline, credit_admin_line] if admintf > 0 else [debit_line, credit_line]
+
+ self.env['account.move.line'].create(journal_line)
return {
'name': _('Journal Entries'),
diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py
index 36570e8f..ef1a5cf4 100644
--- a/indoteknik_custom/models/res_partner.py
+++ b/indoteknik_custom/models/res_partner.py
@@ -194,6 +194,11 @@ class ResPartner(models.Model):
default=_default_payment_term, tracking=3
)
+ previous_payment_term_id = fields.Many2one(
+ 'account.payment.term',
+ string='Previous Payment Term'
+ )
+
@api.depends("street", "street2", "city", "state_id", "country_id", "blok", "nomor", "rt", "rw", "kelurahan_id",
"kecamatan_id")
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 39830ffc..f80941d2 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -398,6 +398,23 @@ class SaleOrder(models.Model):
compute="_compute_partner_is_cbd_locked"
)
+ def action_open_partial_delivery_wizard(self):
+ self.ensure_one()
+ pickings = self.picking_ids.filtered(lambda p: p.state not in ['done', 'cancel'] and p.name and 'BU/PICK/' in p.name)
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'Partial Delivery',
+ 'res_model': 'partial.delivery.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_sale_id': self.id,
+ # kasih langsung list of int biar ga ribet di wizard
+ 'default_picking_ids': pickings.ids,
+ }
+ }
+
+
@api.depends('partner_id.is_cbd_locked')
def _compute_partner_is_cbd_locked(self):
for order in self:
@@ -406,13 +423,13 @@ class SaleOrder(models.Model):
@api.constrains('payment_term_id', 'partner_id', 'state')
def _check_cbd_lock_sale_order(self):
- # cbd_term = self.env['account.payment.term'].browse(26)
+ cbd_term = self.env['account.payment.term'].browse(26)
for rec in self:
if rec.state == 'draft' and rec.partner_id.is_cbd_locked:
- # if rec.payment_term_id and rec.payment_term_id != cbd_term:
- raise ValidationError(
- "Customer ini terkunci ke CBD, hanya boleh pakai Payment Term CBD."
- )
+ if rec.payment_term_id and rec.payment_term_id != cbd_term:
+ raise ValidationError(
+ "Customer ini terkunci ke CBD, hanya boleh pakai Payment Term CBD."
+ )
@api.depends('invoice_ids.payment_state', 'invoice_ids.amount_total', 'invoice_ids.amount_residual')
def _compute_payment_state_custom(self):
@@ -1748,9 +1765,54 @@ class SaleOrder(models.Model):
# sinkronkan ke field commitment_date
rec.commitment_date = rec.expected_ready_to_ship
+ # def _validate_expected_ready_ship_date(self):
+ # """
+ # Pastikan expected_ready_to_ship tidak lebih awal dari SLA minimum.
+ # Dipanggil setiap onchange / simpan SO.
+ # """
+ # for rec in self:
+ # if not rec.expected_ready_to_ship:
+ # continue
+ #
+ # # ADDED: gunakan "sekarang" lokal user, bukan datetime.now() server
+ # current_date = fields.Datetime.context_timestamp(rec, fields.Datetime.now())
+ #
+ # # Hitung SLA
+ # products = rec.order_line
+ # if products:
+ # sla_data = rec.calculate_sla_by_vendor(products)
+ # max_sla_time = sla_data.get('slatime', 1)
+ # else:
+ # max_sla_time = 1
+ #
+ # # offset hari libur/weekend
+ # offset, is3pm = rec.get_days_until_next_business_day(current_date)
+ # min_days = max_sla_time + offset - 1
+ # eta_minimum = current_date + timedelta(days=min_days)
+ #
+ # if rec._fields['expected_ready_to_ship'].type == 'date':
+ # exp_date_local = rec.expected_ready_to_ship
+ # else:
+ # exp_date_local = fields.Datetime.context_timestamp(
+ # rec, rec.expected_ready_to_ship
+ # ).date()
+ #
+ # if exp_date_local < eta_minimum.date():
+ # # (opsional) auto-set ke minimum → konversi balik ke UTC naive bila field Datetime
+ # if rec._fields['expected_ready_to_ship'].type == 'date':
+ # rec.expected_ready_to_ship = eta_minimum.date()
+ # else:
+ # rec.expected_ready_to_ship = eta_minimum.astimezone(pytz.UTC).replace(tzinfo=None)
+ #
+ # raise ValidationError(
+ # _("Tanggal 'Expected Ready to Ship' tidak boleh "
+ # "lebih kecil dari %(tgl)s. Mohon pilih minimal %(tgl)s.")
+ # % {'tgl': eta_minimum.strftime('%d-%m-%Y')}
+ # )
+ # else:
+ # rec.commitment_date = rec.expected_ready_to_ship
-
-
+
@api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship
def _onchange_expected_ready_ship_date(self):
self._validate_expected_ready_ship_date()
@@ -2214,7 +2276,7 @@ class SaleOrder(models.Model):
raise UserError("Terdapat DUPLIKASI data pada Product {}".format(line.product_id.display_name))
def sale_order_approve(self):
- self.check_duplicate_product()
+ # self.check_duplicate_product()
self.check_product_bom()
self.check_credit_limit()
self.check_limit_so_to_invoice()
@@ -2484,7 +2546,7 @@ class SaleOrder(models.Model):
for order in self:
order._validate_delivery_amt()
order._validate_uniform_taxes()
- order.check_duplicate_product()
+ # order.check_duplicate_product()
order.check_product_bom()
order.check_credit_limit()
order.check_limit_so_to_invoice()
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 35d408a1..67106073 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -1352,6 +1352,19 @@ class StockPicking(models.Model):
if self.picking_type_code == 'outgoing' and 'BU/OUT/' in self.name:
self.check_koli()
res = super(StockPicking, self).button_validate()
+
+ # Penambahan link PO di Stock Journal untuk Picking BD
+ for picking in self:
+ if picking.name and 'BD/' in picking.name and picking.purchase_id:
+ stock_journal = self.env['account.move'].search([
+ ('ref', 'ilike', picking.name + '%'),
+ ('journal_id', '=', 3) # Stock Journal ID
+ ], limit = 1)
+ if stock_journal:
+ stock_journal.write({
+ 'purchase_order_id': picking.purchase_id.id
+ })
+
self.date_done = datetime.datetime.utcnow()
self.state_reserve = 'done'
self.final_seq = 0
diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py
index 88acf83c..53a85f67 100644
--- a/indoteknik_custom/models/stock_picking_return.py
+++ b/indoteknik_custom/models/stock_picking_return.py
@@ -110,7 +110,7 @@ class ReturnPicking(models.TransientModel):
if mapping_koli_vals:
context['default_mapping_koli_ids'] = mapping_koli_vals
- if picking.purchase_id or 'PO' in (picking.origin or ''):
+ if picking.name and any(k in picking.name.upper() for k in ('PUT', 'INPUT')):
_logger.info("Redirect ke Tukar Guling PO via purchase_id / origin")
return {
'name': _('Tukar Guling PO'),
diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml
index c88effd5..ba86277a 100644
--- a/indoteknik_custom/views/account_move.xml
+++ b/indoteknik_custom/views/account_move.xml
@@ -59,6 +59,10 @@
<attribute name="widget">pdf_viewer</attribute>
</field>
<field name="invoice_user_id" position="after">
+ <field name="payment_difficulty" widget="badge"
+ decoration-info="payment_difficulty == 'normal'"
+ decoration-warning="payment_difficulty in ('agak_sulit', 'sulit')"
+ decoration-danger="payment_difficulty == 'bermasalah'"/>
<field name="invoice_origin"/>
<field name="date_kirim_tukar_faktur"/>
<field name="shipper_faktur_id"/>
@@ -122,6 +126,11 @@
decoration-danger="mark_upload_efaktur == 'belum_upload'"
decoration-success="mark_upload_efaktur == 'sudah_upload'" />
<field name="due_extension" optional="hide"/>
+ <field name="payment_difficulty" widget="badge"
+ decoration-info="payment_difficulty == 'normal'"
+ decoration-warning="payment_difficulty in ('agak_sulit', 'sulit')"
+ decoration-danger="payment_difficulty == 'bermasalah'"
+ optional="hide"/>
</field>
<field name="payment_state" position="after">
<field name="invoice_payment_term_id" optional="hide"/>
diff --git a/indoteknik_custom/views/account_move_line.xml b/indoteknik_custom/views/account_move_line.xml
index 02b936f1..017a9eda 100644
--- a/indoteknik_custom/views/account_move_line.xml
+++ b/indoteknik_custom/views/account_move_line.xml
@@ -6,6 +6,9 @@
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
+ <xpath expr="//page[@id='aml_tab']/field[@name='line_ids']" position="attributes">
+ <attribute name="attrs">{'readonly': [('refund_id','!=',False)]}</attribute>
+ </xpath>
<xpath expr="//page[@id='aml_tab']/field[@name='line_ids']/tree/field[@name='currency_id']" position="before">
<field name="is_required" invisible="1"/>
</xpath>
diff --git a/indoteknik_custom/views/refund_sale_order.xml b/indoteknik_custom/views/refund_sale_order.xml
index 51f3deab..afa7c1cb 100644
--- a/indoteknik_custom/views/refund_sale_order.xml
+++ b/indoteknik_custom/views/refund_sale_order.xml
@@ -217,6 +217,7 @@
<field name="finance_note"/>
</group>
<group>
+ <field name="biaya_admin"/>
<field name="bukti_refund_type" reqiured="1"/>
<field name="bukti_transfer_refund_pdf" widget="pdf_viewer" attrs="{'invisible': [('bukti_refund_type', '=', 'image')]}"/>
<field name="bukti_transfer_refund_image" widget="image" attrs="{'invisible': [('bukti_refund_type', '=', 'pdf')]}"/>
diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml
index c32151d8..72751187 100644
--- a/indoteknik_custom/views/res_partner.xml
+++ b/indoteknik_custom/views/res_partner.xml
@@ -21,6 +21,7 @@
<field name="reference_number"/>
</field>
<field name="property_payment_term_id" position="after">
+ <field name="previous_payment_term_id" readonly="1"/>
<field name="is_cbd_locked" readonly="1"/>
<field name="user_payment_terms_sales" readonly="1"/>
<field name="date_payment_terms_sales" readonly="1"/>
diff --git a/indoteknik_custom/views/unpaid_invoice_view.xml b/indoteknik_custom/views/unpaid_invoice_view.xml
index e56bbee7..ec6c749d 100644
--- a/indoteknik_custom/views/unpaid_invoice_view.xml
+++ b/indoteknik_custom/views/unpaid_invoice_view.xml
@@ -46,6 +46,10 @@
<field name="date_kirim_tukar_faktur"/>
<field name="date_terima_tukar_faktur"/>
<field name="payment_term_id"/>
+ <button name="action_create_surat_piutang"
+ type="object"
+ string="Create Surat Piutang"
+ class="oe_highlight"/>
</group>
<group>
<field name="sale_id"/>
@@ -57,10 +61,10 @@
decoration-warning="payment_state == 'partial'"/>
<field name="amount_total"/>
<field name="amount_residual"/>
- <button name="action_create_surat_piutang"
- type="object"
- string="Create Surat Piutang"
- class="oe_highlight"/>
+ <field name="payment_difficulty" widget="badge"
+ decoration-info="payment_difficulty == 'normal'"
+ decoration-warning="payment_difficulty in ('agak_sulit', 'sulit')"
+ decoration-danger="payment_difficulty == 'bermasalah'"/>
</group>
</group>
</sheet>