summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIndoteknik . <it@fixcomart.co.id>2025-06-20 16:06:00 +0700
committerIndoteknik . <it@fixcomart.co.id>2025-06-20 16:06:00 +0700
commit6e8591a6bd28c4faafc08eb9c539fe24bdecf419 (patch)
tree35b4b3371d9ae6b7e5a963f3a64be77674750b87
parent6d222cdfb56df09e61cd3add3c3fb328bd9adc7b (diff)
(andri) tracking webhook aktif dan menggantikan peran button sebelumnya
-rw-r--r--indoteknik_api/controllers/api_v1/stock_picking.py33
-rwxr-xr-xindoteknik_custom/models/__init__.py2
-rw-r--r--indoteknik_custom/models/stock_picking.py167
-rw-r--r--indoteknik_custom/views/stock_picking.xml4
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"