summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIndoteknik . <it@fixcomart.co.id>2025-06-04 08:52:38 +0700
committerIndoteknik . <it@fixcomart.co.id>2025-06-04 08:52:38 +0700
commite43e3d0da19e5cbbcbadec40c252c24d2921149c (patch)
treefcde7a8b7596a51fc9065fcd89a47f9d679dcf06
parent43d180117e90db9115f07ab4b5b2880c32594bea (diff)
(andri) fix bug SLA pada quotation SO
-rwxr-xr-xindoteknik_custom/models/sale_order.py172
1 files changed, 106 insertions, 66 deletions
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index a86d43cb..3e340c60 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -306,78 +306,71 @@ class SaleOrder(models.Model):
@api.onchange('carrier_id')
def _onchange_carrier_id(self):
- # Jika record masih baru (belum disimpan), jangan jalankan onchange
+ # ───────────────────────────────────────────────────────────────
+ # 1. abaikan onchange kalau SO masih draft / belum tersimpan
+ # ───────────────────────────────────────────────────────────────
if not self._origin or not self._origin.id:
return
+ sale_order_id = self._origin.id # id SO asli (sudah tersimpan)
+
+ # ───────────────────────────────────────────────────────────────
+ # 2. Jika SO BELUM mempunyai satupun shipping.option ⇒
+ # jangan lakukan validasi apa-apa; cukup reset field & domain
+ # ───────────────────────────────────────────────────────────────
+ total_so_options = self.env['shipping.option'].search_count(
+ [('sale_order_id', '=', sale_order_id)]
+ )
+ if total_so_options == 0:
+ # belum pernah estimasi ongkir ⇒ biarkan user ganti carrier
+ self.shipping_option_id = False
+ return {'domain': {'shipping_option_id': [('id', '=', -1)]}}
+
+ # ───────────────────────────────────────────────────────────────
+ # 3. (kode lama) – mulai validasi hanya jika sudah ada option
+ # ───────────────────────────────────────────────────────────────
self.shipping_option_id = False
- # self.delivery_amt = 0
- # self.delivery_service_type = False
if not self.carrier_id:
return {'domain': {'shipping_option_id': [('id', '=', -1)]}}
- # Ambil provider dari mapping
+ # cari provider dari mapping rajaongkir_kurir
self.env.cr.execute("""
- SELECT name FROM rajaongkir_kurir
+ SELECT name FROM rajaongkir_kurir
WHERE delivery_carrier_id = %s
LIMIT 1
""", (self.carrier_id.id,))
- result = self.env.cr.fetchone()
- provider = result[0].lower() if result and result[0] else False
-
- if not provider:
- provider = self.carrier_id.name.lower().split()[0] if self.carrier_id.name else False
+ row = self.env.cr.fetchone()
+ provider = row[0].lower() if row and row[0] else (
+ self.carrier_id.name.lower().split()[0] if self.carrier_id.name else False
+ )
_logger.info(f"[Carrier Changed] {self.carrier_id.name}, Detected Provider: {provider}")
- sale_order_id = self._origin.id
-
- # Cek apakah ada shipping_option yang cocok
+ # hitung berapa option yg match provider BARU
self.env.cr.execute("""
SELECT COUNT(*) FROM shipping_option
WHERE LOWER(provider) LIKE %s AND sale_order_id = %s
""", (f'%{provider}%', sale_order_id))
- count = self.env.cr.fetchone()[0]
-
- _logger.info(f"[Shipping Option Count] Provider: {provider} | SO ID: {sale_order_id} | Count: {count}")
-
- if count == 0:
- previous_carrier = self._origin.carrier_id if self._origin else False
- previous_provider = False
-
- self.carrier_id = previous_carrier
- self.shipping_option_id = self._origin.shipping_option_id if self._origin else False
-
- # Ambil kembali domain provider sebelumnya
- if previous_carrier:
- self.env.cr.execute("""
- SELECT name FROM rajaongkir_kurir
- WHERE delivery_carrier_id = %s LIMIT 1
- """, (previous_carrier.id,))
- prev_row = self.env.cr.fetchone()
- previous_provider = prev_row[0].lower() if prev_row and prev_row[0] else previous_carrier.name.lower().split()[0]
-
- fallback_domain = [('id', '=', -1)]
- if previous_provider:
- fallback_domain = [
- '|',
- '&', ('sale_order_id', '=', sale_order_id), ('provider', 'ilike', f'%{previous_provider}%'),
- '&', ('sale_order_id', '=', False), ('provider', 'ilike', f'%{previous_provider}%')
- ]
+ matched = self.env.cr.fetchone()[0] or 0
+ if matched == 0:
+ # provider baru tidak ada di option yang SUDAH dibuat → kembalikan ke carrier lama
+ prev_carrier = self._origin.carrier_id
+ self.carrier_id = prev_carrier
+ self.shipping_option_id = self._origin.shipping_option_id or False
return {
'warning': {
'title': "Shipping Option Tidak Ditemukan",
'message': (
- f"Layanan kurir tidak tersedia untuk pengiriman ini.\n"
- f"Pilihan dikembalikan ke sebelumnya."
+ "Layanan kurir tidak tersedia untuk pengiriman ini.\n"
+ "Pilihan dikembalikan ke sebelumnya."
)
},
- 'domain': {'shipping_option_id': fallback_domain}
+ 'domain': {'shipping_option_id': [('id', '=', -1)]}
}
- # Jika data ada, kembalikan domain biasa
+ # kalau match ada → set domain normal (hanya option dengan provider itu)
domain = [
'|',
'&', ('sale_order_id', '=', sale_order_id), ('provider', 'ilike', f'%{provider}%'),
@@ -1230,30 +1223,77 @@ class SaleOrder(models.Model):
self._calculate_etrts_date()
+ # def _validate_expected_ready_ship_date(self):
+ # for rec in self:
+ # if not rec.order_line:
+ # _logger.info("⏩ Lewati validasi ERTS karena belum ada produk.")
+ # return # Lewati validasi jika belum ada produk
+
+ # now = fields.Datetime.now()
+ # expected_date = rec.expected_ready_to_ship and rec.expected_ready_to_ship.date() or None
+ # if not expected_date:
+ # return # Tidak validasi jika tidak ada input sama sekali
+
+ # sla = rec.calculate_sla_by_vendor()
+ # offset_day, lewat_jam_3 = rec.get_days_until_next_business_day()
+ # eta_minimum = now + timedelta(days=sla + offset_day)
+
+ # if expected_date < eta_minimum.date():
+ # rec.expected_ready_to_ship = eta_minimum
+ # raise ValidationError(
+ # "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}."
+ # .format(eta_minimum.strftime('%d-%m-%Y'), eta_minimum.strftime('%d-%m-%Y'))
+ # )
+
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 rec.expected_ready_to_ship and rec.commitment_date:
- current_date = datetime.now()
- # Hanya membandingkan tanggal saja, tanpa jam
- expected_date = rec.expected_ready_to_ship.date()
-
- max_slatime = 1 # Default SLA jika tidak ada
- slatime = self.calculate_sla_by_vendor(rec.order_line)
- max_slatime = max(max_slatime, slatime['slatime'])
-
- offset , is3pm = self.get_days_until_next_business_day(current_date)
- sum_days = max_slatime + offset
- sum_days -= 1
- eta_minimum = current_date + timedelta(days=sum_days)
-
- if expected_date < eta_minimum.date():
- rec.expected_ready_to_ship = eta_minimum
- raise ValidationError(
- "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}."
- .format(eta_minimum.strftime('%d-%m-%Y'), eta_minimum.strftime('%d-%m-%Y'))
- )
- else:
- rec.commitment_date = rec.expected_ready_to_ship
+ # ─────────────────────────────────────────────────────
+ # 1. Hanya validasi kalau field sudah terisi
+ # (quotation baru / belum ada tanggal → abaikan)
+ # ─────────────────────────────────────────────────────
+ if not rec.expected_ready_to_ship:
+ continue
+
+ current_date = datetime.now()
+
+ # ─────────────────────────────────────────────────────
+ # 2. Hitung SLA berdasarkan product lines (jika ada)
+ # ─────────────────────────────────────────────────────
+ products = rec.order_line
+ if products:
+ sla_data = rec.calculate_sla_by_vendor(products)
+ max_sla_time = sla_data.get('slatime', 1)
+ else:
+ # belum ada item → gunakan default 1 hari
+ 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)
+
+ # ─────────────────────────────────────────────────────
+ # 3. Validasi - raise error bila terlalu cepat
+ # ─────────────────────────────────────────────────────
+ if rec.expected_ready_to_ship.date() < eta_minimum.date():
+ # set otomatis ke tanggal minimum supaya user tidak perlu
+ # menekan Save dua kali
+ rec.expected_ready_to_ship = eta_minimum
+
+ 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:
+ # sinkronkan ke field commitment_date
+ rec.commitment_date = rec.expected_ready_to_ship
+
+
@api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship