diff options
| author | Indoteknik . <it@fixcomart.co.id> | 2025-05-22 11:14:35 +0700 |
|---|---|---|
| committer | Indoteknik . <it@fixcomart.co.id> | 2025-05-22 11:14:35 +0700 |
| commit | 34174e95638e1337169dfa5f3be56b9ef57021a1 (patch) | |
| tree | 61f515d74baf8bb0f4274c81156f44567649604a | |
| parent | afb745b1e000f4d3c3dba1723ce4a19f44b8c510 (diff) | |
(andri) sync quotation web ke odoo terkait kurir, service, & tarif
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 173 | ||||
| -rwxr-xr-x | indoteknik_custom/views/sale_order.xml | 4 |
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" /> |
