diff options
| author | Indoteknik . <it@fixcomart.co.id> | 2025-06-04 08:52:38 +0700 |
|---|---|---|
| committer | Indoteknik . <it@fixcomart.co.id> | 2025-06-04 08:52:38 +0700 |
| commit | e43e3d0da19e5cbbcbadec40c252c24d2921149c (patch) | |
| tree | fcde7a8b7596a51fc9065fcd89a47f9d679dcf06 | |
| parent | 43d180117e90db9115f07ab4b5b2880c32594bea (diff) | |
(andri) fix bug SLA pada quotation SO
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 172 |
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 |
