summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIndoteknik . <it@fixcomart.co.id>2025-05-22 11:14:35 +0700
committerIndoteknik . <it@fixcomart.co.id>2025-05-22 11:14:35 +0700
commit34174e95638e1337169dfa5f3be56b9ef57021a1 (patch)
tree61f515d74baf8bb0f4274c81156f44567649604a
parentafb745b1e000f4d3c3dba1723ce4a19f44b8c510 (diff)
(andri) sync quotation web ke odoo terkait kurir, service, & tarif
-rwxr-xr-xindoteknik_custom/models/sale_order.py173
-rwxr-xr-xindoteknik_custom/views/sale_order.xml4
2 files changed, 99 insertions, 78 deletions
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index bcc4d5c4..5a5255b3 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -64,6 +64,7 @@ class ShippingOption(models.Model):
price = fields.Float(string="Price", required=True)
provider = fields.Char(string="Provider")
etd = fields.Char(string="Estimated Delivery Time")
+ courier_service_code = fields.Char(string="Courier Service Code")
sale_order_id = fields.Many2one('sale.order', string="Sale Order", ondelete="cascade")
class SaleOrder(models.Model):
@@ -253,7 +254,7 @@ class SaleOrder(models.Model):
select_shipping_option = fields.Selection([
('biteship', 'Biteship'),
('custom', 'Custom'),
- ], string='Select Shipping Option', help="Select shipping option for delivery", tracking=True)
+ ], string='Shipping Option', help="Select shipping option for delivery", tracking=True)
def get_biteship_carrier_ids(self):
courier_codes = tuple(self._get_biteship_courier_codes() or [])
@@ -603,8 +604,8 @@ class SaleOrder(models.Model):
product_names = '<br/>'.join(missing_weight_products)
self.message_post(body=f"Produk berikut tidak memiliki berat:<br/>{product_names}")
- if total_weight == 0:
- raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.")
+ # if total_weight == 0:
+ # raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.")
# Validasi alamat pengiriman
if not self.real_shipping_id:
@@ -623,13 +624,11 @@ class SaleOrder(models.Model):
def action_estimate_shipping_biteship(self):
total_weight = self._validate_for_shipping_estimate()
-
- # Konversi berat ke gram untuk Biteship
+
weight_gram = int(total_weight * 1000)
if weight_gram < 100:
- weight_gram = 100 # Minimum weight untuk Biteship
-
- # Persiapkan data item
+ weight_gram = 100
+
items = [{
"name": "Paket Pesanan",
"description": f"Sale Order {self.name}",
@@ -640,26 +639,21 @@ class SaleOrder(models.Model):
"width": 10,
"length": 10
}]
-
- # Coba dapatkan data koordinat dari alamat pengiriman
+
shipping_address = self.real_shipping_id
_logger.info(f"Shipping Address: {shipping_address}")
-
- # Data asal (tetap gudang Bandengan)
+
origin_data = {
"origin_latitude": -6.3031123,
"origin_longitude": 106.7794934,
}
-
- # Prioritaskan penggunaan koordinat jika tersedia
+
destination_data = {}
use_coordinate = False
-
- # Cek apakah latitude dan longitude tersedia dan valid
+
if hasattr(shipping_address, 'latitude') and hasattr(shipping_address, 'longtitude'):
if shipping_address.latitude and shipping_address.longtitude:
try:
- # Validasi format koordinat
lat = float(shipping_address.latitude)
lng = float(shipping_address.longtitude)
destination_data = {
@@ -671,79 +665,72 @@ class SaleOrder(models.Model):
except (ValueError, TypeError):
_logger.warning(f"Invalid coordinates, falling back to postal code")
use_coordinate = False
-
- # Jika koordinat tidak tersedia atau tidak valid, gunakan kode pos
+
if not use_coordinate:
if shipping_address.zip:
- origin_data = {"origin_postal_code": 14440} # Reset origin untuk mode kode pos
+ origin_data = {"origin_postal_code": 14440}
destination_data = {
"destination_postal_code": shipping_address.zip
}
_logger.info(f"Using postal code: {shipping_address.zip}")
else:
raise UserError("Tidak dapat mengestimasikan ongkir: Kode pos tujuan tidak tersedia.")
-
- # Siapkan daftar kurir
+
couriers = ','.join(self._get_biteship_courier_codes())
-
- # Panggil API Biteship dengan format yang benar
+
api_mode = "koordinat" if use_coordinate else "kode_pos"
_logger.info(f"Calling Biteship API with mode: {api_mode}")
-
+
result = self._call_biteship_api(origin_data, destination_data, items, couriers)
-
+
if not result:
raise UserError("Gagal mendapatkan estimasi ongkir dari Biteship.")
-
- # Hapus shipping_option lama
+
self.env["shipping.option"].search([('sale_order_id', '=', self.id)]).unlink()
-
- # Proses hasil API
+
shipping_options = []
- courier_options = {} # Dictionary untuk mengelompokkan opsi per kurir
+ courier_options = {}
shipping_services = result.get('pricing', [])
-
+
_logger.info(f"Ditemukan {len(shipping_services)} layanan pengiriman")
-
+
for service in shipping_services:
courier_code = service.get('courier_code', '').lower()
courier_name = service.get('courier_name', '')
service_name = service.get('courier_service_name', '')
- price = service.get('price', 0)
-
+ raw_price = service.get('price', 0)
+ markup_price = int(raw_price * 1.1)
+ price = round(markup_price / 1000) * 1000
+
_logger.info(f"Layanan: {courier_name} - {service_name}, Harga: {price}")
-
- # Lewati layanan dengan harga 0
+
if not price:
_logger.warning(f"Melewati layanan dengan harga 0: {courier_name} - {service_name}")
continue
-
- # Format estimasi waktu
+
duration = service.get('duration', '')
shipment_range = service.get('shipment_duration_range', '')
shipment_unit = service.get('shipment_duration_unit', 'days')
-
- # Gunakan duration jika tersedia, jika tidak, buat dari range
+
if duration:
etd = duration
elif shipment_range:
etd = f"{shipment_range} {shipment_unit}"
else:
- etd = "1-3 days" # Default fallback
-
- # Buat shipping option
+ etd = "1-3 days"
+
try:
shipping_option = self.env["shipping.option"].create({
"name": f"{courier_name} - {service_name}",
"price": price,
"provider": courier_code,
"etd": etd,
+ "courier_service_code": service.get('courier_service_code'),
"sale_order_id": self.id,
})
-
+
shipping_options.append(shipping_option)
-
- # Kelompokkan opsi berdasarkan kurir
+
courier_upper = courier_code.upper()
if courier_upper not in courier_options:
courier_options[courier_upper] = []
@@ -752,87 +739,72 @@ class SaleOrder(models.Model):
"etd": etd,
"price": price
})
-
+
_logger.info(f"Berhasil membuat opsi pengiriman: {courier_name} - {service_name}")
except Exception as e:
_logger.error(f"Gagal membuat opsi pengiriman: {str(e)}")
-
- # Jika tidak ada opsi pengiriman
+
if not shipping_options:
raise UserError(f"Tidak ada layanan pengiriman ditemukan untuk kode pos {destination_data.get('destination_postal_code', '')}. Mohon periksa kembali kode pos atau gunakan metode pengiriman lain.")
-
- # Set opsi sesuai dengan carrier yang sudah dipilih, atau set opsi pertama sebagai default
+
selected_option = None
if self.carrier_id:
- # Dapatkan kode kurir dari carrier
rajaongkir_kurir = self.env['rajaongkir.kurir'].search([
('delivery_carrier_id', '=', self.carrier_id.id)
], limit=1)
-
- # Jika ditemukan rajaongkir_kurir, cari shipping option yang sesuai
+
if rajaongkir_kurir:
courier_code = rajaongkir_kurir.name.lower()
carrier_name = self.carrier_id.name.lower()
-
- # Mencoba beberapa kemungkinan format untuk pencocokan
+
possible_codes = [
courier_code,
carrier_name,
carrier_name.split()[0] if ' ' in carrier_name else carrier_name
]
-
+
_logger.info(f"Mencari shipping option untuk kurir: {possible_codes}")
-
- # Coba temukan shipping option yang sesuai dengan carrier
+
for option in shipping_options:
option_provider = option.provider.lower() if option.provider else ''
option_name = option.name.lower() if option.name else ''
-
- # Cek pencocokan untuk provider atau nama
+
for code in possible_codes:
if code in option_provider or code in option_name:
selected_option = option
_logger.info(f"Menemukan shipping option yang cocok: {option.name}")
break
-
+
if selected_option:
break
- # Jika tidak ada opsi yang cocok dengan carrier, gunakan opsi pertama
if not selected_option and shipping_options:
selected_option = shipping_options[0]
_logger.info(f"Menggunakan opsi pertama: {selected_option.name}")
- # Set shipping option yang terpilih
if selected_option:
self.shipping_option_id = selected_option.id
self.delivery_amt = selected_option.price
-
- # Format untuk pesan log
+
if use_coordinate:
origin_info = f"Koordinat ({origin_data.get('origin_latitude')}, {origin_data.get('origin_longitude')})"
destination_info = f"Koordinat ({destination_data.get('destination_latitude')}, {destination_data.get('destination_longitude')})"
else:
origin_info = f"Kode Pos {origin_data.get('origin_postal_code')}"
destination_info = f"Kode Pos {destination_data.get('destination_postal_code')}"
-
- # PENTING: Gunakan HTML untuk teks preformatted agar jarak baris terjaga
+
message_lines = [f"<b>Estimasi Ongkos Kirim Biteship ({origin_info} → {destination_info}):</b><br/>"]
-
- # Format setiap kurir dan layanannya
+
for courier, options in courier_options.items():
message_lines.append(f"<b>{courier}:</b><br/>")
for opt in options:
message_lines.append(f"Service: {opt['name']}, ETD: {opt['etd']}, Cost: Rp {int(opt['price']):,}<br/>")
- # Tambahkan baris kosong setelah setiap kurir (kecuali yang terakhir)
if courier != list(courier_options.keys())[-1]:
message_lines.append("<br/>")
-
- # Gabungkan baris pesan dengan HTML line breaks
+
message_body = "".join(message_lines)
-
- # Log hasil estimasi dengan format yang diinginkan
+
self.message_post(
body=message_body,
message_type="comment"
@@ -2290,10 +2262,59 @@ class SaleOrder(models.Model):
order_line.discount = discount
order_line.order_id.use_button = True
+ def _auto_set_shipping_from_website(self):
+ for order in self:
+ # Jalankan hanya untuk SO dari website (ID 59)
+ if not order.source_id or order.source_id.id != 59:
+ continue
+
+ # Jika shipping method adalah Self Pick Up (ID 32), atur ke custom
+ if order.carrier_id and order.carrier_id.id == 32:
+ order.select_shipping_option = 'custom'
+ continue
+
+ # Set shipping option ke biteship dan jalankan estimasi
+ order.select_shipping_option = 'biteship'
+ order.action_estimate_shipping()
+
+ if not (order.delivery_service_type and order.carrier_id):
+ continue
+
+ # Ambil provider dari rajaongkir_kurir
+ self.env.cr.execute("""
+ SELECT name FROM rajaongkir_kurir
+ WHERE delivery_carrier_id = %s LIMIT 1
+ """, (order.carrier_id.id,))
+ result = self.env.cr.fetchone()
+ provider = result[0].lower() if result and result[0] else ''
+
+ if not provider and order.carrier_id.name:
+ provider = order.carrier_id.name.lower().split()[0]
+
+ if not provider:
+ _logger.warning(f"[AutoSetShipping] Provider tidak ditemukan untuk carrier_id: {order.carrier_id.id}")
+ continue
+
+ # Cari shipping option berdasarkan provider dan courier_service_code
+ matched_option = self.env['shipping.option'].search([
+ ('sale_order_id', '=', order.id),
+ ('courier_service_code', '=', order.delivery_service_type),
+ ('provider', 'ilike', provider),
+ ], limit=1)
+
+ if matched_option:
+ order.shipping_option_id = matched_option.id
+ order.delivery_amt = matched_option.price
+ _logger.info(f"[AutoSetShipping] Matched via courier_service_code: {matched_option.name} | Provider: {provider}")
+ else:
+ _logger.warning(f"[AutoSetShipping] No match for service code '{order.delivery_service_type}' and provider '{provider}' in SO {order.name}")
+
+
@api.model
def create(self, vals):
# Ensure partner details are updated when a sale order is created
order = super(SaleOrder, self).create(vals)
+ order._auto_set_shipping_from_website()
order._compute_etrts_date()
order._validate_expected_ready_ship_date()
order._validate_delivery_amt()
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 1a0895dc..34397fc7 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -118,10 +118,10 @@
<field name="approval_status" />
<field name="sales_tax_id"
domain="[('type_tax_use','=','sale'), ('active', '=', True)]" required="1" />
- <field name="select_shipping_option" required="1"/>
+ <field name="select_shipping_option"/>
<field name="carrier_id" required="1" domain="[]" />
<field name="delivery_service_type" readonly="1" />
- <field name="shipping_option_id"/>
+ <field name="shipping_option_id" />
</field>
<field name="medium_id" position="after">
<field name="date_doc_kirim" readonly="1" />