diff options
| author | Indoteknik . <it@fixcomart.co.id> | 2025-06-20 16:06:00 +0700 |
|---|---|---|
| committer | Indoteknik . <it@fixcomart.co.id> | 2025-06-20 16:06:00 +0700 |
| commit | 6e8591a6bd28c4faafc08eb9c539fe24bdecf419 (patch) | |
| tree | 35b4b3371d9ae6b7e5a963f3a64be77674750b87 | |
| parent | 6d222cdfb56df09e61cd3add3c3fb328bd9adc7b (diff) | |
(andri) tracking webhook aktif dan menggantikan peran button sebelumnya
| -rw-r--r-- | indoteknik_api/controllers/api_v1/stock_picking.py | 33 | ||||
| -rwxr-xr-x | indoteknik_custom/models/__init__.py | 2 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 167 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_picking.xml | 4 |
4 files changed, 141 insertions, 65 deletions
diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 0926bd26..09d0c585 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -184,13 +184,34 @@ class StockPicking(controller.Controller): # def udpate_status_from_bitehsip(self, **kw): # return "ok" + # def process_order_status(self, data): + # picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], + # limit=1) + # if data.get('status') == 'picked': + # picking_model.write({'driver_departure_date': datetime.utcnow()}) + # elif data.get('status') == 'delivered': + # picking_model.write({'driver_arrival_date': datetime.utcnow()}) + def process_order_status(self, data): - picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], - limit=1) - if data.get('status') == 'picked': - picking_model.write({'driver_departure_date': datetime.utcnow()}) - elif data.get('status') == 'delivered': - picking_model.write({'driver_arrival_date': datetime.utcnow()}) + picking = request.env['stock.picking'].sudo().search([ + ('biteship_id', '=', data.get('order_id')) + ], limit=1) + + if not picking: + _logger.warning(f"[Webhook] Tidak ditemukan picking untuk order_id {data.get('order_id')}") + return + + status = data.get('status') + timestamp = data.get('updated_at') or datetime.utcnow().isoformat() + + description = picking._get_biteship_status_description(status, { + "courier": {"company": data.get("courier_company", "")}, + "destination": {"contact_name": picking.partner_id.name or ""} + }) + + picking.log_biteship_event_from_webhook(status, timestamp, description) + + def process_order_price(self, data): picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 094ac69e..3f538e25 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -151,4 +151,4 @@ from . import account_payment_register from . import stock_inventory from . import sale_order_delay from . import approval_invoice_date -from . import patch +# from . import patch diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index eabef37c..7bb881c2 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1721,15 +1721,15 @@ class StockPicking(models.Model): response = requests.get(_biteship_url + '/trackings/' + self.biteship_tracking_id, headers=headers, json=manifests) result = response.json() - description = { - 'confirmed' : 'Indoteknik telah melakukan permintaan pick-up', - 'allocated' : 'Kurir akan melakukan pick-up pesanan', - 'picking_up' : 'Kurir sedang dalam perjalanan menuju lokasi pick-up', - 'picked' : 'Pesanan sudah di pick-up kurir '+result.get("courier", {}).get("company", ""), - 'on_hold' : 'Pesanan ditahan sementara karena masalah pengiriman', - 'dropping_off' : 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli', - 'delivered' : f'Pesanan telah sampai dan diterima oleh <span style="color:#DC2626;">{result.get("destination", {}).get("contact_name", "")}</span>' - } + # description = { + # 'confirmed' : 'Indoteknik telah melakukan permintaan pick-up', + # 'allocated' : 'Kurir akan melakukan pick-up pesanan', + # 'picking_up' : 'Kurir sedang dalam perjalanan menuju lokasi pick-up', + # 'picked' : 'Pesanan sudah di pick-up kurir '+result.get("courier", {}).get("company", ""), + # 'on_hold' : 'Pesanan ditahan sementara karena masalah pengiriman', + # 'dropping_off' : 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli', + # 'delivered' : f'Pesanan telah sampai dan diterima oleh <span style="color:#DC2626;">{result.get("destination", {}).get("contact_name", "")}</span>' + # } if (result.get('success') == True): history = result.get("history", []) status = result.get("status", "") @@ -1738,7 +1738,7 @@ class StockPicking(models.Model): manifests.append({ "status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(), "datetime": self._convert_to_local_time(entry["updated_at"]), - "description": description[entry["status"]], + "description": self._get_biteship_status_description(entry["status"], result), }) return { @@ -1754,53 +1754,108 @@ class StockPicking(models.Model): _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") return { 'error': str(e) } - def action_sync_biteship_tracking(self): - for picking in self: - if not picking.biteship_id: - raise UserError("Tracking Biteship tidak tersedia.") - - histori = picking.get_manifest_biteship() - updated_fields = {} - seen_logs = set() - - manifests = sorted(histori.get("manifests", []), key=lambda m: m.get("datetime") or "") - - for manifest in manifests: - status = manifest.get("status", "").lower() - dt_str = manifest.get("datetime") - desc = manifest.get("description") - dt = False - - try: - dt = picking._convert_to_utc_datetime(dt_str) - _logger.info(f"[Biteship Sync] Berhasil parse datetime: {dt_str} -> {dt}") - except Exception as e: - _logger.warning(f"[Biteship Sync] Gagal parse datetime: {e}") - continue + # def action_sync_biteship_tracking(self): + # for picking in self: + # if not picking.biteship_id: + # raise UserError("Tracking Biteship tidak tersedia.") + + # histori = picking.get_manifest_biteship() + # updated_fields = {} + # seen_logs = set() + + # manifests = sorted(histori.get("manifests", []), key=lambda m: m.get("datetime") or "") + + # for manifest in manifests: + # status = manifest.get("status", "").lower() + # dt_str = manifest.get("datetime") + # desc = manifest.get("description") + # dt = False + + # try: + # dt = picking._convert_to_utc_datetime(dt_str) + # _logger.info(f"[Biteship Sync] Berhasil parse datetime: {dt_str} -> {dt}") + # except Exception as e: + # _logger.warning(f"[Biteship Sync] Gagal parse datetime: {e}") + # continue + + # # Update tanggal ke field (pastikan naive datetime UTC) + # if status == "picked" and dt and not picking.driver_departure_date: + # updated_fields["driver_departure_date"] = fields.Datetime.to_string(dt) + + # if status == "delivered" and dt and not picking.driver_arrival_date: + # updated_fields["driver_arrival_date"] = fields.Datetime.to_string(dt) + + # # Buat log unik dengan waktu lokal Asia/Jakarta + # if dt and desc: + # try: + # dt_local = parser.parse(dt_str).replace(tzinfo=None) + # except Exception as e: + # _logger.warning(f"[Biteship Sync] Gagal parse dt_str untuk log: {e}") + # dt_local = dt # fallback + + # desc_clean = ' '.join(desc.strip().split()) + # log_line = f"[TRACKING] {status} - {dt_local.strftime('%d %b %Y %H:%M')}: {desc_clean}" + # if not picking._has_existing_log(log_line): + # picking.message_post(body=log_line) + # seen_logs.add(log_line) + + # if updated_fields: + # picking.write(updated_fields) + + def _get_biteship_status_description(self, status, data=None): + + data = data or {} + + courier = data.get("courier", {}).get("company", "") + contact_name = data.get("destination", {}).get("contact_name", "") + + description_map = { + 'confirmed': 'Indoteknik telah melakukan permintaan pick-up', + 'allocated': 'Kurir akan melakukan pick-up pesanan', + 'picking_up': 'Kurir sedang dalam perjalanan menuju lokasi pick-up', + 'picked': f'Pesanan sudah di pick-up kurir {courier}', + 'on_hold': 'Pesanan ditahan sementara karena masalah pengiriman', + 'dropping_off': 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli', + 'delivered': f'Pesanan telah sampai dan diterima oleh <span style="color:#DC2626;">{contact_name}</span>', + 'cancelled': 'Pesanan dibatalkan oleh sistem atau pengguna', + } + + return description_map.get(status, f"Status '{status}' diterima dari Biteship") + + + def log_biteship_event_from_webhook(self, status, timestamp, description): + self.ensure_one() + updated_fields = {} + + try: + dt = self._convert_to_utc_datetime(timestamp) + except Exception as e: + _logger.warning(f"[Webhook] Gagal konversi waktu: {e}") + dt = datetime.utcnow() + + if status == "picked" and not self.driver_departure_date: + updated_fields["driver_departure_date"] = fields.Datetime.to_string(dt) + if status == "delivered" and not self.driver_arrival_date: + updated_fields["driver_arrival_date"] = fields.Datetime.to_string(dt) + + # Update shipping_status + shipping_status = self._map_status_biteship(status) + if shipping_status and self.shipping_status != shipping_status: + updated_fields["shipping_status"] = shipping_status + + # Log ke chatter + try: + dt_local = parser.parse(timestamp).replace(tzinfo=None) + except Exception: + dt_local = dt + + log_line = f"[TRACKING] {status} - {dt_local.strftime('%d %b %Y %H:%M')}: {description.strip()}" + if not self._has_existing_log(log_line): + self.message_post(body=log_line) + + if updated_fields: + self.write(updated_fields) - # Update tanggal ke field (pastikan naive datetime UTC) - if status == "picked" and dt and not picking.driver_departure_date: - updated_fields["driver_departure_date"] = fields.Datetime.to_string(dt) - - if status == "delivered" and dt and not picking.driver_arrival_date: - updated_fields["driver_arrival_date"] = fields.Datetime.to_string(dt) - - # Buat log unik dengan waktu lokal Asia/Jakarta - if dt and desc: - try: - dt_local = parser.parse(dt_str).replace(tzinfo=None) - except Exception as e: - _logger.warning(f"[Biteship Sync] Gagal parse dt_str untuk log: {e}") - dt_local = dt # fallback - - desc_clean = ' '.join(desc.strip().split()) - log_line = f"[TRACKING] {status} - {dt_local.strftime('%d %b %Y %H:%M')}: {desc_clean}" - if not picking._has_existing_log(log_line): - picking.message_post(body=log_line) - seen_logs.add(log_line) - - if updated_fields: - picking.write(updated_fields) def _has_existing_log(self, log_line): self.ensure_one() diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index c088e00c..22e6cf60 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -64,12 +64,12 @@ string="Biteship" type="object" /> - <button name="action_sync_biteship_tracking" + <!-- <button name="action_sync_biteship_tracking" type="object" string="Lacak dari Biteship" class="btn-primary" attrs="{'invisible': [('biteship_id', '=', False)]}" - /> + /> --> <button name="track_envio_shipment" string="Tracking Envio" type="object" |
