summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-08-14 16:28:15 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-08-14 16:28:15 +0700
commitbfdaf12dc3e7abace11502fcf1cb6c87522f1a51 (patch)
tree1b7d44f2b6a5ad2539f181effb66e9e3a59c9e9a
parentad6ccce331dc8c83f2cacb82b0e3c94467ff9d85 (diff)
parentdb75081a2afcd369e8a0169f3fe2f080dbad0c4a (diff)
Merge branch 'odoo-backup' of bitbucket.org:altafixco/indoteknik-addons into odoo-backup
-rw-r--r--indoteknik_api/controllers/api_v1/partner.py4
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py21
-rw-r--r--indoteknik_api/models/sale_order.py10
-rw-r--r--indoteknik_custom/models/account_move.py73
-rw-r--r--indoteknik_custom/models/res_partner.py4
-rwxr-xr-xindoteknik_custom/models/sale_order.py30
-rw-r--r--indoteknik_custom/models/stock_picking.py8
-rw-r--r--indoteknik_custom/models/tukar_guling.py60
-rw-r--r--indoteknik_custom/models/tukar_guling_po.py60
-rw-r--r--indoteknik_custom/report/report_sale_order.xml6
-rw-r--r--indoteknik_custom/views/tukar_guling.xml9
-rw-r--r--indoteknik_custom/views/tukar_guling_po.xml10
12 files changed, 205 insertions, 90 deletions
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py
index b8bd21be..acec19f7 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': [''],
'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': [''],
'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/account_move.py b/indoteknik_custom/models/account_move.py
index fd08ed60..b0ffd8b9 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':
@@ -119,20 +121,6 @@ class AccountMove(models.Model):
else:
move.payment_date = False
- # def name_get(self):
- # result = []
- # for move in self:
- # if move.move_type == 'entry':
- # # Jika masih draft, tampilkan 'Draft CAB'
- # if move.state == 'draft':
- # label = 'Draft CAB'
- # else:
- # label = move.name
- # result.append((move.id, label))
- # else:
- # # Untuk invoice dan lainnya, pakai default
- # result.append((move.id, move.display_name))
- # return result
def send_due_invoice_reminder(self):
today = fields.Date.today()
@@ -144,31 +132,22 @@ class AccountMove(models.Model):
today + timedelta(days=7),
]
- # --- 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),
- # ])
+ for days_after_due in range(14, 181, 7):
+ target_dates.append(today - timedelta(days=days_after_due))
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),
+ ('date_terima_tukar_faktur', '!=', False)
])
- _logger.info(f"Invoices tahap 1: {invoices}")
+ _logger.info(f"Invoices: {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}")
+ # _logger.info(f"Invoices tahap 2: {invoices}")
if not invoices:
_logger.info("Tidak ada invoice yang due")
@@ -177,11 +156,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),
@@ -190,11 +176,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
@@ -208,7 +199,7 @@ class AccountMove(models.Model):
invoice_table_rows += f"""
<tr>
<td>{inv.partner_id.name}</td>
- <td>{inv.purchase_order_id.name or '-'}</td>
+ <td>{inv.ref or '-'}</td>
<td>{inv.name}</td>
<td>{fields.Date.to_string(inv.invoice_date) or '-'}</td>
<td>{fields.Date.to_string(inv.invoice_date_due) or '-'}</td>
@@ -220,9 +211,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 = (
@@ -244,9 +235,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 = (
@@ -282,18 +273,18 @@ 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',
}
- _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'],
diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py
index f260f58e..cf9fbea4 100644
--- a/indoteknik_custom/models/res_partner.py
+++ b/indoteknik_custom/models/res_partner.py
@@ -231,7 +231,7 @@ class ResPartner(models.Model):
rec.payment_difficulty = rec.parent_id.payment_difficulty
return records
- @api.constrains('name')
+ @api.constrains('email')
def _check_duplicate_name(self):
for record in self:
if record.name:
@@ -242,7 +242,7 @@ class ResPartner(models.Model):
], limit=1)
if existing_partner:
- raise ValidationError(f"Nama '{record.name}' sudah digunakan oleh partner lain!")
+ raise ValidationError(f"Nama '{record.name}' dengan email '{record.email}' sudah digunakan oleh partner lain!")
@api.constrains('npwp')
def _check_npwp(self):
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index e71e3830..0acfa0b0 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"<b>Estimasi Ongkos Kirim Biteship:</b><br/>"]
for courier, options in courier_options.items():
@@ -2178,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")
@@ -2408,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'
@@ -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
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 0354587c..bf6834d0 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):
@@ -1714,7 +1714,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 = {
diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py
index 4c7722d5..4b03d4b0 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,19 +83,47 @@ class TukarGuling(models.Model):
invoice_id = fields.Many2many('account.move', string='Invoice Ref', readonly=True)
- @api.depends('origin')
+ @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', '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:
@@ -144,8 +172,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 +203,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 +359,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
diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py
index 0badc117..cc1c79c0 100644
--- a/indoteknik_custom/models/tukar_guling_po.py
+++ b/indoteknik_custom/models/tukar_guling_po.py
@@ -62,20 +62,57 @@ 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')
+ @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', '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:
@@ -133,6 +170,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/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 @@
<th name="th_description" class="text-left">Description</th>
<th name="th_quantity" class="text-right">Quantity</th>
<th name="th_priceunit" class="text-right">Unit Price</th>
- <th name="th_discount" t-if="display_discount" class="text-right" groups="product.group_discount_per_so_line">
- <span>Disc.%</span>
+ <th name="th_discount" class="text-right" groups="product.group_discount_per_so_line">
+ Disc.%
</th>
<th name="th_taxes" class="text-right">Taxes</th>
<th name="th_subtotal" class="text-right">
@@ -125,7 +125,7 @@
<td name="td_priceunit" class="text-right">
<span t-field="line.price_unit"/>
</td>
- <td t-if="display_discount" class="text-right" groups="product.group_discount_per_so_line">
+ <td class="text-right" groups="product.group_discount_per_so_line">
<span t-field="line.discount"/>
</td>
<td name="td_taxes" class="text-right">
diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml
index 20aa2eef..9dd31905 100644
--- a/indoteknik_custom/views/tukar_guling.xml
+++ b/indoteknik_custom/views/tukar_guling.xml
@@ -21,7 +21,7 @@
<field name="name">pengajuan.tukar.guling.tree</field>
<field name="model">tukar.guling</field>
<field name="arch" type="xml">
- <tree create="1" delete="1" default_order="create_date desc">
+ <tree create="0" delete="1" default_order="create_date desc">
<field name="name"/>
<field name="partner_id" string="Customer"/>
<field name="origin" string="SO Number"/>
@@ -83,9 +83,9 @@
<field name="return_type" attrs="{'readonly': [('state', 'not in', 'draft')]}"/>
<field name="operations"
attrs="{'readonly': [('state', 'not in', 'draft')]}"/>
- <field name="origin" readonly="1"/>
+<!-- <field name="origin" readonly="1"/>-->
+ <field name="origin_so" readonly="1"/>
<field name="is_has_invoice" readonly="1"/>
- <field name="invoice_id" readonly="1" widget="many2many_tags"/>
</group>
<group>
<field name="val_inv_opt" attrs="{'invisible': [('is_has_invoice', '=', False)]}"/>
@@ -95,6 +95,9 @@
<field name="date_finance" readonly="1"/>
<field name="date_logistic" readonly="1"/>
</group>
+ <group>
+ <field name="invoice_id" readonly="1" attrs="{'invisible': [('is_has_invoice', '=', False)]}"/>
+ </group>
</group>
<notebook>
<page string="Product Lines" name="product_lines">
diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml
index bc696a3d..1c6a86ea 100644
--- a/indoteknik_custom/views/tukar_guling_po.xml
+++ b/indoteknik_custom/views/tukar_guling_po.xml
@@ -24,7 +24,7 @@
<tree create="1" delete="1" default_order="create_date desc">
<field name="name"/>
<field name="vendor_id" string="Customer"/>
- <field name="origin" string="SO Number"/>
+ <field name="origin" string="PO Number"/>
<field name="operations" string="Operations"/>
<field name="return_type" string="Return Type"/>
<field name="state" widget="badge"
@@ -89,11 +89,10 @@
attrs="{
'required': [('return_type', 'in', ['revisi_po', 'tukar_guling'])]
}"/>
- <field name="origin" readonly="1"/>
+<!-- <field name="origin" readonly="1"/>-->
+ <field name="origin_po" readonly="1"/>
<field name="is_has_bill" readonly="1"/>
<!-- <field name="bill_id" readonly="1" />-->
- <field name="bill_id" readonly="1" widget="many2many_tags"/>
- <!-- <field name="origin_so" readonly="1"/>-->
</group>
<group>
<field name="val_bil_opt" attrs="{'invisible': [('is_has_bill', '=', False)]}"/>
@@ -103,6 +102,9 @@
<field name="date_finance" readonly="1"/>
<field name="date_logistic" readonly="1"/>
</group>
+ <group>
+ <field name="bill_id" readonly="1" attrs="{'invisible': [('is_has_bill', '=', False)]}"/>
+ </group>
</group>
<!-- Product Lines -->
<notebook>