From 3ca53ea0afef07cb79040c9f3c5aa29fa2355c90 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 27 Mar 2025 08:48:27 +0700 Subject: sale order delay --- indoteknik_custom/models/stock_picking.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6c6cbaa1..a11bf29f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -16,8 +16,8 @@ import re _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" -# _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" +# _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" @@ -1241,7 +1241,7 @@ class StockPicking(models.Model): '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("name", ""), + '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' : 'Pesanan telah sampai dan diterima oleh '+result.get("destination", {}).get("contact_name", "") -- cgit v1.2.3 From 337e86c31691544a49a04e3f8d3a4b259e6b126a Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 10 Apr 2025 08:51:12 +0700 Subject: testing biteship dinamis eta --- indoteknik_custom/models/stock_picking.py | 91 +++++++++++-------------------- 1 file changed, 33 insertions(+), 58 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index a11bf29f..b741e94e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -16,8 +16,8 @@ import re _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -# _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" -_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" +_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +# _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" @@ -189,63 +189,12 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - # estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') - # countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) - # countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Remaining Time') def schduled_update_sequance(self): query = "SELECT update_sequance_stock_picking();" self.env.cr.execute(query) - - - # @api.depends('estimated_ready_ship_date', 'state') - # def _callculate_sequance(self): - # for record in self: - # try : - # if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): - # rts = record.estimated_ready_ship_date - waktu.now() - # rts_days = rts.days - # rts_hours = divmod(rts.seconds, 3600) - - # estimated_by_erts = rts.total_seconds() / 3600 - - # record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" - # record.countdown_hours = estimated_by_erts - # else: - # record.countdown_hours = 999999999999 - # record.countdown_ready_to_ship = False - # except Exception as e : - # _logger.error(f"Error calculating sequance {record.id}: {str(e)}") - # print(str(e)) - # return { 'error': str(e) } - - - # @api.depends('estimated_ready_ship_date', 'state') - # def _compute_countdown_hours(self): - # for record in self: - # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: - # # Gunakan nilai yang sangat besar sebagai placeholder - # record.countdown_hours = 999999 - # else: - # delta = record.estimated_ready_ship_date - waktu.now() - # record.countdown_hours = delta.total_seconds() / 3600 - - # @api.depends('estimated_ready_ship_date', 'state') - # def _compute_countdown_ready_to_ship(self): - # for record in self: - # if record.state in ('cancel', 'done'): - # record.countdown_ready_to_ship = False - # else: - # if record.estimated_ready_ship_date: - # delta = record.estimated_ready_ship_date - waktu.now() - # days = delta.days - # hours, remainder = divmod(delta.seconds, 3600) - # record.countdown_ready_to_ship = f"{days} days, {hours} hours" - # record.countdown_hours = delta.total_seconds() / 3600 - # else: - # record.countdown_ready_to_ship = False def _compute_lalamove_image_html(self): for record in self: @@ -1182,6 +1131,8 @@ class StockPicking(models.Model): self.ensure_one() order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) + + sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', order.name)], limit=1) response = { 'delivery_order': { @@ -1197,13 +1148,24 @@ class StockPicking(models.Model): 'delivery_status': None, 'eta': self.generate_eta_delivery(), 'is_biteship': True if self.biteship_id else False, - 'manifests': self.get_manifests() + 'manifests': self.get_manifests(), + 'is_delay': True if sale_order_delay and sale_order_delay.status == 'delayed' else False } if self.biteship_id : histori = self.get_manifest_biteship() - eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start) - eta_end = order.date_order + timedelta(days=order.estimated_arrival_days) + day_start = order.estomated_arrival_days_start + day_end = order.estomated_arrival_days + if sale_order_delay: + if sale_order_delay.status == 'delayed': + day_start = day_start + sale_order_delay.days_delayed + day_end = day_end + sale_order_delay.days_delayed + elif sale_order_delay.status == 'early': + day_start = day_start - sale_order_delay.days_delayed + day_end = day_end - sale_order_delay.days_delayed + + eta_start = order.date_order + timedelta(days=day_start) + eta_end = order.date_order + timedelta(days=day_end) formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" response['eta'] = formatted_eta response['manifests'] = histori.get("manifests", []) @@ -1297,18 +1259,31 @@ class StockPicking(models.Model): current_date = datetime.datetime.now() prepare_days = 3 start_date = self.driver_departure_date or self.create_date + + + add_day_start = 0 + add_day_end = 0 + sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', self.sale_id.name)], limit=1) + if sale_order_delay: + if sale_order_delay.status == 'delayed': + add_day_start = sale_order_delay.days_delayed + add_day_end = sale_order_delay.days_delayed + elif sale_order_delay.status == 'early': + add_day_start = -abs(sale_order_delay.days_delayed) + add_day_end = -abs(sale_order_delay.days_delayed) ead = self.sale_id.estimated_arrival_days or 0 if not self.driver_departure_date: ead += prepare_days ead_datetime = datetime.timedelta(days=ead) - fastest_eta = start_date + ead_datetime + fastest_eta = start_date + ead_datetime + datetime.timedelta(days=add_day_start) + if not self.driver_departure_date and fastest_eta < current_date: fastest_eta = current_date + ead_datetime longest_days = 3 - longest_eta = fastest_eta + datetime.timedelta(days=longest_days) + longest_eta = fastest_eta + datetime.timedelta(days=longest_days + add_day_end) format_time = '%d %b %Y' format_time_fastest = '%d %b' if fastest_eta.year == longest_eta.year else format_time -- cgit v1.2.3 From 62f9c93c02a1f8b12ecd7bf50f850c43dd7c2c49 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 11 Apr 2025 10:14:08 +0700 Subject: dirver departure date --- indoteknik_custom/models/stock_picking.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index b741e94e..d7e8e0e8 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -16,8 +16,8 @@ import re _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" -# _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" +# _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" @@ -1142,6 +1142,7 @@ class StockPicking(models.Model): 'receiver_name': '', 'receiver_city': '' }, + 'delivered_date': self.driver_departure_date or False, 'delivered': False, 'status': self.shipping_status, 'waybill_number': self.delivery_tracking_no or '', @@ -1154,8 +1155,8 @@ class StockPicking(models.Model): if self.biteship_id : histori = self.get_manifest_biteship() - day_start = order.estomated_arrival_days_start - day_end = order.estomated_arrival_days + day_start = order.estimated_arrival_days_start + day_end = order.estimated_arrival_days if sale_order_delay: if sale_order_delay.status == 'delayed': day_start = day_start + sale_order_delay.days_delayed @@ -1192,7 +1193,6 @@ class StockPicking(models.Model): "Content-Type": "application/json" } - manifests = [] try: @@ -1206,7 +1206,7 @@ class StockPicking(models.Model): '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' : 'Pesanan telah sampai dan diterima oleh '+result.get("destination", {}).get("contact_name", "") + 'delivered' : f'Pesanan telah sampai dan diterima oleh {result.get("destination", {}).get("contact_name", "")}' } if(result.get('success') == True): history = result.get("history", []) -- cgit v1.2.3 From ef00237c7b6b3aed4f6040d1f124199d3551561e Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 11 Apr 2025 14:50:53 +0700 Subject: expected delivery date manifest --- indoteknik_custom/models/stock_picking.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index d7e8e0e8..54256299 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1137,15 +1137,15 @@ class StockPicking(models.Model): response = { 'delivery_order': { 'name': self.name, - 'carrier': self.carrier_id.name or '', - 'service' : order.delivery_service_type or '', + 'carrier': self.carrier_id.name or '-', + 'service' : order.delivery_service_type or '-', 'receiver_name': '', 'receiver_city': '' }, - 'delivered_date': self.driver_departure_date or False, + 'delivered_date': self.driver_departure_date.strftime('%d %b %Y') if self.driver_departure_date != False else '-', 'delivered': False, 'status': self.shipping_status, - 'waybill_number': self.delivery_tracking_no or '', + 'waybill_number': self.delivery_tracking_no or '-', 'delivery_status': None, 'eta': self.generate_eta_delivery(), 'is_biteship': True if self.biteship_id else False, @@ -1205,7 +1205,7 @@ class StockPicking(models.Model): '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', + 'dropping_off' : 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli', 'delivered' : f'Pesanan telah sampai dan diterima oleh {result.get("destination", {}).get("contact_name", "")}' } if(result.get('success') == True): -- cgit v1.2.3 From 6eb0b48ad5c418f565efdf1a60d221a10465b0b8 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 14 Apr 2025 16:48:09 +0700 Subject: stock picking mapping --- indoteknik_custom/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 54256299..ba7a9452 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -16,8 +16,8 @@ import re _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -# _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" -_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" +_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +# _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" -- cgit v1.2.3 From fb50d10576f2e5d16faba612dfd1565f7168f655 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 16 Apr 2025 14:33:31 +0700 Subject: FEEDBACK --- indoteknik_custom/models/stock_picking.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 4a200ac5..f2b69b55 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1406,7 +1406,10 @@ class StockPicking(models.Model): "delivered": status } - return manifests + return { + "manifests": [], + "delivered": False + } except Exception as e : _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") return { 'error': str(e) } -- cgit v1.2.3 From d9d8b9f3afc0ad60ca1199b08ab6e2836663a0de Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 24 Apr 2025 13:54:06 +0700 Subject: fixing revisi renca --- indoteknik_custom/models/stock_picking.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f2b69b55..aa616e62 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1442,8 +1442,10 @@ class StockPicking(models.Model): def generate_eta_delivery(self): current_date = datetime.datetime.now() - prepare_days = 3 - start_date = self.driver_departure_date or self.create_date + days_start = self.sale_id.estimated_arrival_days_start or self.sale_id.estimated_arrival_days + days_end = self.sale_id.estimated_arrival_days or (self.sale_id.estimated_arrival_days + 3) + start_date = self.sale_id.create_date + datetime.timedelta(days=days_start) + end_date = self.sale_id.create_date + datetime.timedelta(days=days_end) add_day_start = 0 @@ -1456,19 +1458,10 @@ class StockPicking(models.Model): elif sale_order_delay.status == 'early': add_day_start = -abs(sale_order_delay.days_delayed) add_day_end = -abs(sale_order_delay.days_delayed) - - ead = self.sale_id.estimated_arrival_days or 0 - if not self.driver_departure_date: - ead += prepare_days - - ead_datetime = datetime.timedelta(days=ead) - fastest_eta = start_date + ead_datetime + datetime.timedelta(days=add_day_start) - if not self.driver_departure_date and fastest_eta < current_date: - fastest_eta = current_date + ead_datetime + fastest_eta = start_date +datetime.timedelta(days=add_day_start + add_day_start) - longest_days = 3 - longest_eta = fastest_eta + datetime.timedelta(days=longest_days + add_day_end) + longest_eta = end_date + datetime.timedelta(days=add_day_end) format_time = '%d %b %Y' format_time_fastest = '%d %b' if fastest_eta.year == longest_eta.year else format_time -- cgit v1.2.3 From 914705630f61f2e02f15ee24a479191e945a0f22 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Sat, 26 Apr 2025 08:39:32 +0700 Subject: handle bugs additional time when checkout > 15.00 --- indoteknik_custom/models/stock_picking.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 36129f00..38a1173c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -562,6 +562,10 @@ class StockPicking(models.Model): }) payload = { + "origin_coordinate" :{ + "latitude": -6.3031123, + "longitude" : 106.7794934999 + }, "reference_id " : self.sale_id.name, "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', @@ -585,10 +589,6 @@ class StockPicking(models.Model): # Cek jika pengiriman instant atau same_day if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ - "origin_coordinate" :{ - "latitude": -6.3031123, - "longitude" : 106.7794934999 - }, "destination_coordinate" : { "latitude": self.real_shipping_id.latitude, "longitude": self.real_shipping_id.longtitude, -- cgit v1.2.3 From 509eb9406e6c48caf3e6366c3d8ac60643b71546 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 29 Apr 2025 09:09:05 +0700 Subject: fixing jam 15 --- indoteknik_custom/models/stock_picking.py | 1 - 1 file changed, 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 38a1173c..39c74aa2 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -523,7 +523,6 @@ class StockPicking(models.Model): raise UserError(f"Kesalahan tidak terduga: {str(e)}") def action_send_to_biteship(self): - if self.biteship_tracking_id: raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") -- cgit v1.2.3 From fad209db285b0a6204dc1fcbf2e2e0cb13f872b0 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 23 May 2025 09:05:27 +0700 Subject: (andri) penyesuaian data quotation SO ke delivery biteship --- indoteknik_custom/models/stock_picking.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 39c74aa2..5548db75 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -565,7 +565,7 @@ class StockPicking(models.Model): "latitude": -6.3031123, "longitude" : 106.7794934999 }, - "reference_id " : self.sale_id.name, + "reference_id" : self.sale_id.name, "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', "shipper_organization": self.carrier_id.name, @@ -585,6 +585,8 @@ class StockPicking(models.Model): "items": items_data_standard } + _logger.info(f"Payload untuk Biteship: {payload}") + # Cek jika pengiriman instant atau same_day if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ @@ -603,6 +605,7 @@ class StockPicking(models.Model): # Kirim request ke Biteship response = requests.post(_biteship_url+'/orders', headers=headers, json=payload) + _logger.info(f"Response dari Biteship: {response.text}") if response.status_code == 200: data = response.json() -- cgit v1.2.3 From b7cb8418e18de4507bc81d04e8c150f78343b92e Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 27 May 2025 10:09:54 +0700 Subject: validation qty demand --- indoteknik_custom/models/stock_picking.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0fcb7ca1..cbfcda22 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1269,6 +1269,20 @@ class StockPicking(models.Model): current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.date_reserved = current_time + # Validate Qty Demand Can't higher than Qty Product + for move_line in self.move_line_ids_without_package: + purchase_line = move_line.move_id.purchase_line_id + if purchase_line: + if purchase_line.product_uom_qty < move_line.product_uom_qty: + raise UserError( + _("Quantity demand (%s) tidak bisa lebih besar dari qty product (%s) untuk produk %s") % ( + move_line.product_uom_qty, + purchase_line.product_uom_qty, + move_line.product_id.display_name + ) + ) + + self.validation_minus_onhand_quantity() self.responsible = self.env.user.id # self.send_koli_to_so() -- cgit v1.2.3 From a01f05f50dd852e76b44709259e8baaf33e4b462 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 27 May 2025 10:35:08 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0fcb7ca1..05f946fd 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -288,7 +288,7 @@ class StockPicking(models.Model): self.ensure_one() if not self.name or not self.origin: return False - return f"{self.name} {self.origin}" + return f"{self.name}, {self.origin}" def _download_pod_photo(self, url): """Mengunduh foto POD dari URL""" -- cgit v1.2.3 From af127b94e7b597a3451f42199df56e27899272fd Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 27 May 2025 10:45:11 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 05f946fd..887b2f92 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -625,7 +625,7 @@ class StockPicking(models.Model): ('state', '=', 'done'), ('carrier_id', '=', 151) ]) - for picking in pickings: + for picking in self: if not picking.name: raise UserError("Name pada stock.picking tidak ditemukan.") -- cgit v1.2.3 From 755f6efe380cbbdd05ba592f651ca87030a22143 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 27 May 2025 10:57:24 +0700 Subject: fix api shipment group --- indoteknik_custom/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 887b2f92..3223377c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -310,10 +310,10 @@ class StockPicking(models.Model): except ValueError: return False - def action_get_kgx_pod(self): + def action_get_kgx_pod(self, shipment=False): self.ensure_one() - awb_number = self._get_kgx_awb_number() + awb_number = shipment or self._get_kgx_awb_number() if not awb_number: raise UserError("Nomor AWB tidak dapat dibuat, pastikan picking memiliki name dan origin") -- cgit v1.2.3 From ae9766498d304a8336737453310e4272929cea73 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 27 May 2025 11:20:09 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 3223377c..ea52450e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -619,18 +619,19 @@ class StockPicking(models.Model): except ValueError: raise UserError(f"Format waktu tidak sesuai: {date_str}") - def track_envio_shipment(self): + def track_envio_shipment(self, shipment=False): pickings = self.env['stock.picking'].search([ ('picking_type_code', '=', 'outgoing'), ('state', '=', 'done'), ('carrier_id', '=', 151) ]) for picking in self: + name = shipment or picking.name if not picking.name: raise UserError("Name pada stock.picking tidak ditemukan.") # API URL dan headers - url = f"https://api.envio.co.id/v1/tracking/distribution?code={picking.name}" + url = f"https://api.envio.co.id/v1/tracking/distribution?code={name}" headers = { 'Authorization': 'Bearer JZ0Seh6qpYJAC3CJHdhF7sPqv8B/uSSfZe1VX5BL?vPYdo', 'Content-Type': 'application/json', -- cgit v1.2.3 From 4ed94e9e1de027aba326ff3dce954b765f752009 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 28 May 2025 09:00:30 +0700 Subject: (andri) penambahan log note ketika biteship berhasil & penyesuaian field required pada SO --- indoteknik_custom/models/stock_picking.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 5548db75..4522dac0 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -617,6 +617,14 @@ class StockPicking(models.Model): waybill_id = data.get("courier", {}).get("waybill_id", "") + self.message_post( + body=f"📦 Biteship berhasil dilakukan.
" + f"Kurir: {self.carrier_id.name}
" + f"Tracking ID: {self.biteship_tracking_id or '-'}
" + f"Resi: {waybill_id or '-'}", + message_type="comment" + ) + message = f"✅ Berhasil Order ke Biteship! Resi: {waybill_id}" if waybill_id else "⚠️ Order berhasil, tetapi tidak ada nomor resi." return { -- cgit v1.2.3 From 9b1ff130cfcec243c698ff4a8b2a5412d3e79011 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 28 May 2025 14:47:16 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ea52450e..e83ab13f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -288,7 +288,7 @@ class StockPicking(models.Model): self.ensure_one() if not self.name or not self.origin: return False - return f"{self.name}, {self.origin}" + return f"{self.name} {self.origin}" def _download_pod_photo(self, url): """Mengunduh foto POD dari URL""" -- cgit v1.2.3 From 6e56ffc2c5b5ee22bc97d2518274eeb8a959e80e Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 28 May 2025 15:16:40 +0700 Subject: (andri) penyesuaian request biteship di stock picking --- indoteknik_custom/models/stock_picking.py | 73 ++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 26 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 4522dac0..54da700b 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -526,46 +526,55 @@ class StockPicking(models.Model): if self.biteship_tracking_id: raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") - # Mencari data sale.order.line berdasarkan sale_id + # Fungsi bantu: menentukan apakah kurir perlu koordinat + def is_courier_need_coordinates(service_code): + return service_code in [ + "instant", "same_day", "instant_car", + "instant_bike", "motorcycle", "mpv", "van", "truck", + "cdd_bak", "cdd_box", "engkel_box", "engkel_bak" + ] + + # Ambil order line products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)]) - - # Fungsi untuk membangun items_data dari order lines + + # Bangun data items untuk standard def build_items_data(lines): return [{ "name": line.product_id.name, "description": line.name, "value": line.price_unit, "quantity": line.product_uom_qty, - "weight": line.weight + "weight": line.weight*1000 } for line in lines] - # Items untuk pengiriman standard items_data_standard = build_items_data(products) - # Items untuk pengiriman instant, mengambil product_id dari move_line_ids_without_package + # Bangun data items untuk pengiriman instant items_data_instant = [] for move_line in self.move_line_ids_without_package: - # Mencari baris di sale.order.line berdasarkan product_id dari move_line order_line = self.env['sale.order.line'].search([ ('order_id', '=', self.sale_id.id), ('product_id', '=', move_line.product_id.id) ], limit=1) - + if order_line: items_data_instant.append({ "name": order_line.product_id.name, "description": order_line.name, "value": order_line.price_unit, "quantity": move_line.qty_done, - "weight": order_line.weight + "weight": order_line.weight*1000 }) + _logger.info(f"Items data standard: {items_data_standard}") + _logger.info(f"Items data instant: {items_data_instant}") + # Bangun payload dasar payload = { - "origin_coordinate" :{ + "origin_coordinate": { "latitude": -6.3031123, - "longitude" : 106.7794934999 + "longitude": 106.7794934999 }, - "reference_id" : self.sale_id.name, + "reference_id": self.sale_id.name, "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', "shipper_organization": self.carrier_id.name, @@ -581,30 +590,40 @@ class StockPicking(models.Model): "courier_type": self.sale_id.delivery_service_type or "reg", "courier_company": self.carrier_id.name.lower(), "delivery_type": "now", - "destination_postal_code": self.real_shipping_id.zip, "items": items_data_standard } - _logger.info(f"Payload untuk Biteship: {payload}") + _logger.info(f"Delivery service type: {self.sale_id.delivery_service_type}") + _logger.info(f"Carrier: {self.carrier_id.name}") + _logger.info(f"Payload awal: {payload}") + + # Tambahkan destination_coordinate jika diperlukan + if is_courier_need_coordinates(self.sale_id.delivery_service_type): + if not self.real_shipping_id.latitude or not self.real_shipping_id.longtitude: + raise UserError("Alamat tujuan tidak memiliki koordinat (latitude/longitude).") + + # items_to_use = items_data_instant if items_data_instant else items_data_standard + if not items_data_instant: + raise UserError("Pengiriman instant membutuhkan produk yang sudah diproses (qty_done > 0). Harap lakukan validasi picking terlebih dahulu.") - # Cek jika pengiriman instant atau same_day - if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ - "destination_coordinate" : { + "destination_coordinate": { "latitude": self.real_shipping_id.latitude, "longitude": self.real_shipping_id.longtitude, }, "items": items_data_instant }) - + + _logger.info(f"Payload untuk Biteship: {payload}") + + # Kirim ke Biteship api_key = _biteship_api_key headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } - # Kirim request ke Biteship - response = requests.post(_biteship_url+'/orders', headers=headers, json=payload) + response = requests.post(_biteship_url + '/orders', headers=headers, json=payload) _logger.info(f"Response dari Biteship: {response.text}") if response.status_code == 200: @@ -614,11 +633,11 @@ class StockPicking(models.Model): self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") self.delivery_tracking_no = data.get("courier", {}).get("waybill_id", "") - - waybill_id = data.get("courier", {}).get("waybill_id", "") + + waybill_id = self.biteship_waybill_id self.message_post( - body=f"📦 Biteship berhasil dilakukan.
" + body=f"Biteship berhasil dilakukan.
" f"Kurir: {self.carrier_id.name}
" f"Tracking ID: {self.biteship_tracking_id or '-'}
" f"Resi: {waybill_id or '-'}", @@ -629,16 +648,18 @@ class StockPicking(models.Model): return { 'effect': { - 'fadeout': 'slow', # Efek menghilang perlahan - 'message': message, # Pesan sukses - 'type': 'rainbow_man', # Efek animasi lucu Odoo + 'fadeout': 'slow', + 'message': message, + 'type': 'rainbow_man', } } + else: error_data = response.json() error_message = error_data.get("error", "Unknown error") error_code = error_data.get("code", "No code provided") raise UserError(f"Error saat mengirim ke Biteship: {error_message} (Code: {error_code})") + @api.constrains('driver_departure_date') def constrains_driver_departure_date(self): -- cgit v1.2.3 From c652ee3e1f652d23e37833f01e3fdd7aa8e52021 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 31 May 2025 11:03:06 +0700 Subject: (andri) add tracking biteship pada BU OUT & fix delivery departure & arrival date --- indoteknik_custom/models/stock_picking.py | 55 ++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 54da700b..4d38e5b3 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1551,7 +1551,60 @@ class StockPicking(models.Model): except Exception as e : _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: + local_dt_str = picking._convert_to_local_time(dt_str) + dt = fields.Datetime.from_string(local_dt_str) + except Exception as e: + _logger.warning(f"[Biteship Sync] Gagal parse datetime: {e}") + continue + + # Update tanggal ke field + if status == "picked" and dt and not picking.driver_departure_date: + updated_fields["driver_departure_date"] = dt + + if status == "delivered" and dt and not picking.driver_arrival_date: + updated_fields["driver_arrival_date"] = dt + + # Buat log unik + if dt and desc: + desc_clean = ' '.join(desc.strip().split()) + log_line = f"[TRACKING] {status} - {dt.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() + self.env.cr.execute(""" + SELECT 1 FROM mail_message + WHERE model = %s AND res_id = %s + AND subtype_id IS NOT NULL + AND body ILIKE %s + LIMIT 1 + """, (self._name, self.id, f"%{log_line}%")) + return self.env.cr.fetchone() is not None + def _convert_to_local_time(self, iso_date): try: dt_with_tz = waktu.fromisoformat(iso_date) -- cgit v1.2.3 From b6928c9bffc486b471d4c335c2550cbc1bf7d841 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 31 May 2025 12:32:45 +0700 Subject: (andri) fix bug datetime departure&arrival --- indoteknik_custom/models/stock_picking.py | 34 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 4d38e5b3..4517a941 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1570,23 +1570,23 @@ class StockPicking(models.Model): dt = False try: - local_dt_str = picking._convert_to_local_time(dt_str) - dt = fields.Datetime.from_string(local_dt_str) + dt = picking._convert_to_local_time(dt_str) except Exception as e: _logger.warning(f"[Biteship Sync] Gagal parse datetime: {e}") continue - # Update tanggal ke field + # Update tanggal ke field (pastikan naive datetime UTC) if status == "picked" and dt and not picking.driver_departure_date: - updated_fields["driver_departure_date"] = dt + 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"] = dt + updated_fields["driver_arrival_date"] = fields.Datetime.to_string(dt) - # Buat log unik + # Buat log unik dengan waktu lokal Asia/Jakarta if dt and desc: + dt_local = pytz.utc.localize(dt).astimezone(pytz.timezone("Asia/Jakarta")) desc_clean = ' '.join(desc.strip().split()) - log_line = f"[TRACKING] {status} - {dt.strftime('%d %b %Y %H:%M')}: {desc_clean}" + 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) @@ -1607,15 +1607,21 @@ class StockPicking(models.Model): def _convert_to_local_time(self, iso_date): try: - dt_with_tz = waktu.fromisoformat(iso_date) - utc_dt = dt_with_tz.astimezone(pytz.utc) - + from dateutil import parser + import pytz + if isinstance(iso_date, str): + waktu = parser.parse(iso_date) + else: + waktu = iso_date + if waktu.tzinfo is None: + waktu = waktu.replace(tzinfo=pytz.utc) local_tz = pytz.timezone("Asia/Jakarta") - local_dt = utc_dt.astimezone(local_tz) - - return local_dt.strftime("%Y-%m-%d %H:%M:%S") + local_dt = waktu.astimezone(local_tz) + utc_dt = local_dt.astimezone(pytz.utc).replace(tzinfo=None) + return utc_dt except Exception as e: - return str(e) + _logger.warning(f"[Biteship] Gagal konversi waktu lokal: {e}") + return False def _map_status_biteship(self, status): status_mapping = { -- cgit v1.2.3 From 43d180117e90db9115f07ab4b5b2880c32594bea Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 2 Jun 2025 12:09:27 +0700 Subject: (andri) fix date tracking --- indoteknik_custom/models/stock_picking.py | 34 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 4517a941..a2935a07 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1570,7 +1570,8 @@ class StockPicking(models.Model): dt = False try: - dt = picking._convert_to_local_time(dt_str) + 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 @@ -1584,7 +1585,12 @@ class StockPicking(models.Model): # Buat log unik dengan waktu lokal Asia/Jakarta if dt and desc: - dt_local = pytz.utc.localize(dt).astimezone(pytz.timezone("Asia/Jakarta")) + 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): @@ -1605,10 +1611,24 @@ class StockPicking(models.Model): """, (self._name, self.id, f"%{log_line}%")) return self.env.cr.fetchone() is not None + # Untuk internal Odoo (mengembalikan naive UTC datetime untuk disimpan ke DB) + def _convert_to_utc_datetime(self, iso_date): + try: + if isinstance(iso_date, str): + waktu = parser.parse(iso_date) + else: + waktu = iso_date + if waktu.tzinfo is None: + waktu = waktu.replace(tzinfo=pytz.utc) + utc_dt = waktu.astimezone(pytz.utc).replace(tzinfo=None) + return utc_dt + except Exception as e: + _logger.warning(f"[Biteship] Gagal konversi waktu UTC: {e}") + return False + + # Untuk tampilan di API atau kebutuhan web (mengembalikan string waktu lokal) def _convert_to_local_time(self, iso_date): try: - from dateutil import parser - import pytz if isinstance(iso_date, str): waktu = parser.parse(iso_date) else: @@ -1617,11 +1637,9 @@ class StockPicking(models.Model): waktu = waktu.replace(tzinfo=pytz.utc) local_tz = pytz.timezone("Asia/Jakarta") local_dt = waktu.astimezone(local_tz) - utc_dt = local_dt.astimezone(pytz.utc).replace(tzinfo=None) - return utc_dt + return local_dt.strftime("%Y-%m-%d %H:%M:%S") except Exception as e: - _logger.warning(f"[Biteship] Gagal konversi waktu lokal: {e}") - return False + return str(e) def _map_status_biteship(self, status): status_mapping = { -- cgit v1.2.3 From 01029f94471df384decb606dbb3088f31a5a8108 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 3 Jun 2025 09:49:28 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index a215eb74..3921ed5a 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -584,6 +584,7 @@ class StockPicking(models.Model): self.lalamove_phone = phone self.lalamove_status = pod.get("status") self.lalamove_delivered_at = delivered_at_dt + self.driver_arrival_date = delivered_at_dt return data raise UserError("No delivered data found in Lalamove response.") -- cgit v1.2.3 From d6f3060b46582ddd78f596ce3527871cae1b2b46 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 3 Jun 2025 14:08:30 +0700 Subject: (andri) ganti referensi dari no SO ke no BU OUT --- indoteknik_custom/models/stock_picking.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index a2935a07..39872ecb 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -568,13 +568,13 @@ class StockPicking(models.Model): _logger.info(f"Items data standard: {items_data_standard}") _logger.info(f"Items data instant: {items_data_instant}") - # Bangun payload dasar + payload = { "origin_coordinate": { "latitude": -6.3031123, "longitude": 106.7794934999 }, - "reference_id": self.sale_id.name, + "reference_id": self.name, # PERUBAHAN: Gunakan nomor BU/OUT "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', "shipper_organization": self.carrier_id.name, @@ -587,6 +587,7 @@ class StockPicking(models.Model): "destination_address": self.real_shipping_id.street, "destination_postal_code": self.real_shipping_id.zip, "origin_note": "BELAKANG INDOMARET", + "destination_note": f"SO: {self.sale_id.name}", # PERUBAHAN: Tambahkan SO ke note "courier_type": self.sale_id.delivery_service_type or "reg", "courier_company": self.carrier_id.name.lower(), "delivery_type": "now", @@ -640,7 +641,9 @@ class StockPicking(models.Model): body=f"Biteship berhasil dilakukan.
" f"Kurir: {self.carrier_id.name}
" f"Tracking ID: {self.biteship_tracking_id or '-'}
" - f"Resi: {waybill_id or '-'}", + f"Resi: {waybill_id or '-'}
" + f"Reference: {self.name}
" + f"SO: {self.sale_id.name}", message_type="comment" ) -- cgit v1.2.3 From b519e0fdac46a64ffef87d27ba824038147d831b Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 3 Jun 2025 15:36:08 +0700 Subject: (andri) memastikan order sesuai dengan informasi barang yang ingin dikirim --- indoteknik_custom/models/stock_picking.py | 98 ++++++++++++------------------- 1 file changed, 39 insertions(+), 59 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 39872ecb..cdff6e32 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -526,7 +526,6 @@ class StockPicking(models.Model): if self.biteship_tracking_id: raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") - # Fungsi bantu: menentukan apakah kurir perlu koordinat def is_courier_need_coordinates(service_code): return service_code in [ "instant", "same_day", "instant_car", @@ -534,47 +533,42 @@ class StockPicking(models.Model): "cdd_bak", "cdd_box", "engkel_box", "engkel_bak" ] - # Ambil order line - products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)]) - - # Bangun data items untuk standard - def build_items_data(lines): - return [{ - "name": line.product_id.name, - "description": line.name, - "value": line.price_unit, - "quantity": line.product_uom_qty, - "weight": line.weight*1000 - } for line in lines] - - items_data_standard = build_items_data(products) + # ✅ Ambil item dari move_line_ids_with_package (qty_done > 0) + items = [] + for ml in self.move_line_ids_without_package: + if ml.qty_done <= 0: + continue - # Bangun data items untuk pengiriman instant - items_data_instant = [] - for move_line in self.move_line_ids_without_package: - order_line = self.env['sale.order.line'].search([ + product = ml.product_id + weight = product.weight or 0.1 # default minimal + line = ml.sale_line_id or self.env['sale.order.line'].search([ ('order_id', '=', self.sale_id.id), - ('product_id', '=', move_line.product_id.id) + ('product_id', '=', product.id) ], limit=1) - if order_line: - items_data_instant.append({ - "name": order_line.product_id.name, - "description": order_line.name, - "value": order_line.price_unit, - "quantity": move_line.qty_done, - "weight": order_line.weight*1000 - }) + value = line.price_unit if line else 0 + description = line.name if line else product.name + + items.append({ + "name": product.name, + "description": description, + "value": value, + "quantity": ml.qty_done, + "weight": int(weight * 1000), + }) - _logger.info(f"Items data standard: {items_data_standard}") - _logger.info(f"Items data instant: {items_data_instant}") + if not items: + raise UserError("Pengiriman tidak dapat dilakukan karena tidak ada barang yang divalidasi (qty_done = 0).") + + shipping_partner = self.real_shipping_id + courier_service_code = self.sale_id.delivery_service_type or "reg" payload = { "origin_coordinate": { "latitude": -6.3031123, "longitude": 106.7794934999 }, - "reference_id": self.name, # PERUBAHAN: Gunakan nomor BU/OUT + "reference_id": self.name, "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', "shipper_organization": self.carrier_id.name, @@ -582,38 +576,26 @@ class StockPicking(models.Model): "origin_contact_phone": "081717181922", "origin_address": "Jl. Bandengan Utara Komp A & BRT. Penjaringan, Kec. Penjaringan, Jakarta (BELAKANG INDOMARET) KOTA JAKARTA UTARA PENJARINGAN", "origin_postal_code": 14440, - "destination_contact_name": self.real_shipping_id.name, - "destination_contact_phone": self.real_shipping_id.phone or self.real_shipping_id.mobile, - "destination_address": self.real_shipping_id.street, - "destination_postal_code": self.real_shipping_id.zip, + "destination_contact_name": shipping_partner.name, + "destination_contact_phone": shipping_partner.phone or shipping_partner.mobile, + "destination_address": shipping_partner.street, + "destination_postal_code": shipping_partner.zip, "origin_note": "BELAKANG INDOMARET", - "destination_note": f"SO: {self.sale_id.name}", # PERUBAHAN: Tambahkan SO ke note - "courier_type": self.sale_id.delivery_service_type or "reg", + "destination_note": f"SO: {self.sale_id.name}", + "courier_type": courier_service_code, "courier_company": self.carrier_id.name.lower(), "delivery_type": "now", - "items": items_data_standard + "items": items } - _logger.info(f"Delivery service type: {self.sale_id.delivery_service_type}") - _logger.info(f"Carrier: {self.carrier_id.name}") - _logger.info(f"Payload awal: {payload}") - - # Tambahkan destination_coordinate jika diperlukan - if is_courier_need_coordinates(self.sale_id.delivery_service_type): - if not self.real_shipping_id.latitude or not self.real_shipping_id.longtitude: + if is_courier_need_coordinates(courier_service_code): + if not shipping_partner.latitude or not shipping_partner.longtitude: raise UserError("Alamat tujuan tidak memiliki koordinat (latitude/longitude).") - # items_to_use = items_data_instant if items_data_instant else items_data_standard - if not items_data_instant: - raise UserError("Pengiriman instant membutuhkan produk yang sudah diproses (qty_done > 0). Harap lakukan validasi picking terlebih dahulu.") - - payload.update({ - "destination_coordinate": { - "latitude": self.real_shipping_id.latitude, - "longitude": self.real_shipping_id.longtitude, - }, - "items": items_data_instant - }) + payload["destination_coordinate"] = { + "latitude": shipping_partner.latitude, + "longitude": shipping_partner.longtitude, + } _logger.info(f"Payload untuk Biteship: {payload}") @@ -633,7 +615,7 @@ class StockPicking(models.Model): self.biteship_id = data.get("id", "") self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") - self.delivery_tracking_no = data.get("courier", {}).get("waybill_id", "") + self.delivery_tracking_no = self.biteship_waybill_id waybill_id = self.biteship_waybill_id @@ -656,13 +638,11 @@ class StockPicking(models.Model): 'type': 'rainbow_man', } } - else: error_data = response.json() error_message = error_data.get("error", "Unknown error") error_code = error_data.get("code", "No code provided") raise UserError(f"Error saat mengirim ke Biteship: {error_message} (Code: {error_code})") - @api.constrains('driver_departure_date') def constrains_driver_departure_date(self): -- cgit v1.2.3 From 8a9c08a21fd7d2ac63ef849d9417b11563092f0d Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 3 Jun 2025 21:10:12 +0700 Subject: (andri) perbaikan biteship pada stock picking --- indoteknik_custom/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index cdff6e32..7e001299 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -541,9 +541,9 @@ class StockPicking(models.Model): product = ml.product_id weight = product.weight or 0.1 # default minimal - line = ml.sale_line_id or self.env['sale.order.line'].search([ + line = ml.move_id.sale_line_id or self.env['sale.order.line'].search([ ('order_id', '=', self.sale_id.id), - ('product_id', '=', product.id) + ('product_id', '=', ml.product_id.id) ], limit=1) value = line.price_unit if line else 0 -- cgit v1.2.3 From 288a7f333c1d37574dba76f3fb3f7216da259cae Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 7 Jun 2025 09:41:27 +0700 Subject: (andri) add field shipping option pada stock picking sebagai tambahan info --- indoteknik_custom/models/stock_picking.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7e001299..d6413b87 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -244,6 +244,11 @@ class StockPicking(models.Model): biteship_waybill_id = fields.Char(string="Biteship Waybill ID") final_seq = fields.Float(string='Remaining Time') shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method SO', related='sale_id.carrier_id') + shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option SO', related='sale_id.shipping_option_id') + select_shipping_option_so = fields.Selection([ + ('biteship', 'Biteship'), + ('custom', 'Custom'), + ], string='Shipping Type SO', related='sale_id.select_shipping_option') state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status') approval_invoice_date_id = fields.Many2one('approval.invoice.date', string='Approval Invoice Date') last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim') -- cgit v1.2.3 From de747091235c844d33bcf62b4d98af0d03251826 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 9 Jun 2025 11:37:19 +0700 Subject: (andri) fix note order biteship --- indoteknik_custom/models/stock_picking.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index d6413b87..d04b3d27 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -587,6 +587,7 @@ class StockPicking(models.Model): "destination_postal_code": shipping_partner.zip, "origin_note": "BELAKANG INDOMARET", "destination_note": f"SO: {self.sale_id.name}", + "order_note": f"SO: {self.sale_id.name}", "courier_type": courier_service_code, "courier_company": self.carrier_id.name.lower(), "delivery_type": "now", -- cgit v1.2.3 From 1a63fac5f7f4dbb2990e5b1eeb9d7f381f39e908 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 9 Jun 2025 20:35:35 +0700 Subject: add products in manifest --- indoteknik_custom/models/stock_picking.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 71eca020..ae3c8f1d 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1646,6 +1646,14 @@ class StockPicking(models.Model): sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', order.name)], limit=1) + product_shipped = [] + for move_line in self.move_line_ids_without_package: + if move_line.qty_done > 0: + product_shipped.append({ + 'name': move_line.product_id.name, + 'qty': move_line.qty_done + }) + response = { 'delivery_order': { 'name': self.name, @@ -1662,7 +1670,8 @@ class StockPicking(models.Model): 'eta': self.generate_eta_delivery(), 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests(), - 'is_delay': True if sale_order_delay and sale_order_delay.status == 'delayed' else False + 'is_delay': True if sale_order_delay and sale_order_delay.status == 'delayed' else False, + 'products': product_shipped } if self.biteship_id: -- cgit v1.2.3 From b6f63fcf96355bc11f9843a9f661cea0a458475e Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 12 Jun 2025 13:17:22 +0700 Subject: fix tracking api lalamove --- indoteknik_custom/models/stock_picking.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 3921ed5a..9dfc8b78 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -529,13 +529,7 @@ class StockPicking(models.Model): record.kgx_pod_photo = "No image available." def action_fetch_lalamove_order(self): - pickings = self.env['stock.picking'].search([ - ('picking_type_code', '=', 'outgoing'), - ('state', '=', 'done'), - ('carrier_id', '=', 9), - ('lalamove_order_id', '!=', False) - ]) - for picking in pickings: + for picking in self: try: order_id = picking.lalamove_order_id apikey = self.env['ir.config_parameter'].sudo().get_param('lalamove.apikey') -- cgit v1.2.3 From 8ac5d556a6686c6b81d5e9178bff5d308e8f176f Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 12 Jun 2025 15:11:06 +0700 Subject: (andri) add validasi tidak bisa kirim biteship (stock picking) jika shipping option SO adalah custom --- indoteknik_custom/models/stock_picking.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ae3c8f1d..8b03e18d 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -690,6 +690,9 @@ class StockPicking(models.Model): def action_send_to_biteship(self): if self.biteship_tracking_id: raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") + + if self.sale_id.select_shipping_option == 'custom': + raise UserError("Shipping Option pada Sales Order ini adalah *Custom*. Tidak dapat dikirim melalui Biteship.") def is_courier_need_coordinates(service_code): return service_code in [ -- cgit v1.2.3 From bb765c2cddb0f630c8cf2a69793aa95f7f8f6f08 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 13 Jun 2025 09:48:01 +0700 Subject: change request params to get data kgx --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 9dfc8b78..622c537e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -288,7 +288,7 @@ class StockPicking(models.Model): self.ensure_one() if not self.name or not self.origin: return False - return f"{self.name} {self.origin}" + return f"{self.name}" def _download_pod_photo(self, url): """Mengunduh foto POD dari URL""" -- cgit v1.2.3 From 819f7c2f0dfe0eaf857c44e80062dcc7e94c9828 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 14 Jun 2025 13:06:43 +0700 Subject: (andri) memindahkan key biteship (test&live) ke system parameter --- indoteknik_custom/models/stock_picking.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 8b03e18d..89e7fecc 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -19,11 +19,6 @@ import re _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" - - -# _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" - class StockPicking(models.Model): _inherit = 'stock.picking' @@ -170,6 +165,10 @@ class StockPicking(models.Model): area_name = fields.Char(string="Area", compute="_compute_area_name") + def _get_biteship_api_key(self): + return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_test') + # return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_live') + @api.depends('real_shipping_id.kecamatan_id', 'real_shipping_id.kota_id') def _compute_area_name(self): for record in self: @@ -769,7 +768,7 @@ class StockPicking(models.Model): _logger.info(f"Payload untuk Biteship: {payload}") # Kirim ke Biteship - api_key = _biteship_api_key + api_key = self._get_biteship_api_key() headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" @@ -1712,7 +1711,7 @@ class StockPicking(models.Model): return response def get_manifest_biteship(self): - api_key = _biteship_api_key + api_key = self._get_biteship_api_key() headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" -- cgit v1.2.3 From 8718c383e39440f83d6579ef536478053e623bc3 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 14 Jun 2025 13:14:24 +0700 Subject: (andri) live biteship --- indoteknik_custom/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 89e7fecc..7775e991 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -166,8 +166,8 @@ class StockPicking(models.Model): area_name = fields.Char(string="Area", compute="_compute_area_name") def _get_biteship_api_key(self): - return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_test') - # return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_live') + # return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_test') + return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_live') @api.depends('real_shipping_id.kecamatan_id', 'real_shipping_id.kota_id') def _compute_area_name(self): -- cgit v1.2.3 From dba82e88291ac63473b8f45dd7a4da5b4d147973 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 16 Jun 2025 14:00:14 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6eac857c..92317334 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1362,7 +1362,7 @@ class StockPicking(models.Model): picking_date = fields.Date.to_date(picking.date_doc_kirim) invoice_date = fields.Date.to_date(invoice.invoice_date) - if picking_date != invoice_date and picking.update_date_doc_kirim_add: + if picking_date != invoice_date and picking.update_date_doc_kirim_add and not picking.so_lama: raise UserError("Tanggal Kirim (%s) tidak sesuai dengan Tanggal Invoice (%s)!" % ( picking_date.strftime('%d-%m-%Y'), invoice_date.strftime('%d-%m-%Y') -- cgit v1.2.3 From 3c8ff8cb6a24dd1ddec7d34313722d7dee6f23a3 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 16 Jun 2025 16:15:57 +0700 Subject: (andri) taruh key hardcode --- indoteknik_custom/models/stock_picking.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6eac857c..f552ff3f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -19,6 +19,9 @@ import re _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" +biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" +# biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" + class StockPicking(models.Model): _inherit = 'stock.picking' @@ -165,9 +168,9 @@ class StockPicking(models.Model): area_name = fields.Char(string="Area", compute="_compute_area_name") - def _get_biteship_api_key(self): - # return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_test') - return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_live') + # def _get_biteship_api_key(self): + # # return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_test') + # return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_live') @api.depends('real_shipping_id.kecamatan_id', 'real_shipping_id.kota_id') def _compute_area_name(self): @@ -762,7 +765,7 @@ class StockPicking(models.Model): _logger.info(f"Payload untuk Biteship: {payload}") # Kirim ke Biteship - api_key = self._get_biteship_api_key() + api_key = biteship_api_key headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" @@ -1705,7 +1708,7 @@ class StockPicking(models.Model): return response def get_manifest_biteship(self): - api_key = self._get_biteship_api_key() + api_key = biteship_api_key headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" -- cgit v1.2.3 From 34d276fe64a92c4a1c75f6fbf8fa961e84c8afc4 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 17 Jun 2025 22:52:39 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index eabef37c..e24bff02 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -26,6 +26,10 @@ biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lI class StockPicking(models.Model): _inherit = 'stock.picking' _order = 'final_seq ASC' + tukar_guling_id = fields.Many2one( + 'tukar.guling', + string='Tukar Guling Reference' + ) konfirm_koli_lines = fields.One2many('konfirm.koli', 'picking_id', string='Konfirm Koli', auto_join=True, copy=False) scan_koli_lines = fields.One2many('scan.koli', 'picking_id', string='Scan Koli', auto_join=True, copy=False) @@ -1196,6 +1200,10 @@ class StockPicking(models.Model): def button_validate(self): self.check_invoice_date() + _logger.info("Kode Picking: %s", self.picking_type_id.code) + _logger.info("Group ID: %s", self.group_id) + _logger.info("Group ID ID: %s", self.group_id.id if self.group_id else None) + _logger.info("Is Internal Use: %s", self.is_internal_use) threshold_datetime = waktu(2025, 4, 11, 6, 26) group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])]) @@ -2391,4 +2399,4 @@ class WarningModalWizard(models.TransientModel): def action_continue(self): if self.picking_id: return self.picking_id.with_context(skip_koli_check=True).button_validate() - return {'type': 'ir.actions.act_window_close'} + return {'type': 'ir.actions.act_window_close'} \ No newline at end of file -- cgit v1.2.3 From 6e8591a6bd28c4faafc08eb9c539fe24bdecf419 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Fri, 20 Jun 2025 16:06:00 +0700 Subject: (andri) tracking webhook aktif dan menggantikan peran button sebelumnya --- indoteknik_custom/models/stock_picking.py | 167 ++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 56 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') 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 {result.get("destination", {}).get("contact_name", "")}' - } + # 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 {result.get("destination", {}).get("contact_name", "")}' + # } 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 {contact_name}', + '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() -- cgit v1.2.3 From 47f83eefa28e7902c4f91c03ac6cd2f71a56e67d Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 21 Jun 2025 11:02:58 +0700 Subject: (andri) penyesuaian informasi delivery (biteship) pada BU OUT --- indoteknik_custom/models/stock_picking.py | 57 ++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 8 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7bb881c2..f171c5d0 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -271,15 +271,22 @@ class StockPicking(models.Model): # Biteship Section biteship_id = fields.Char(string="Biteship Respon ID") - biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") + biteship_tracking_id = fields.Char(string="Biteship Tracking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") + biteship_driver_name = fields.Char('Biteship Driver Name') + biteship_driver_phone = fields.Char('Biteship Driver Phone') + biteship_driver_plate_number = fields.Char('Biteship Driver Plate Number') + biteship_courier_link = fields.Char('Biteship Courier Link') + biteship_shipping_status = fields.Char('Biteship Shipping Status', help="Status pengiriman dari Biteship") + biteship_shipping_price = fields.Monetary('Biteship Shipping Price', currency_field='currency_id', help="Harga pengiriman dari Biteship") + currency_id = fields.Many2one('res.currency', related='sale_id.currency_id', string='Currency', readonly=True) final_seq = fields.Float(string='Remaining Time') - shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method SO', related='sale_id.carrier_id') - shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option SO', related='sale_id.shipping_option_id') + shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id', help="Shipping Method yang digunakan di SO") + shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option', related='sale_id.shipping_option_id' , help="Shipping Option yang digunakan di SO") select_shipping_option_so = fields.Selection([ ('biteship', 'Biteship'), ('custom', 'Custom'), - ], string='Shipping Type SO', related='sale_id.select_shipping_option') + ], string='Shipping Type', related='sale_id.select_shipping_option', help="Shipping Type yang digunakan di SO") state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status') approval_invoice_date_id = fields.Many2one('approval.invoice.date', string='Approval Invoice Date') last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim', copy=False) @@ -1802,6 +1809,16 @@ class StockPicking(models.Model): # if updated_fields: # picking.write(updated_fields) + def action_open_biteship_tracking(self): + self.ensure_one() + if not self.biteship_courier_link: + raise UserError("Biteship tracking link tidak tersedia.") + return { + 'type': 'ir.actions.act_url', + 'url': self.biteship_courier_link, + 'target': 'new', + } + def _get_biteship_status_description(self, status, data=None): data = data or {} @@ -1823,16 +1840,21 @@ class StockPicking(models.Model): return description_map.get(status, f"Status '{status}' diterima dari Biteship") - def log_biteship_event_from_webhook(self, status, timestamp, description): + def log_biteship_event_from_webhook(self, status, timestamp, description, extra_data=None): + """ + extra_data: dict opsional dari webhook (driver name, phone, plate, link, price, dsb) + """ self.ensure_one() updated_fields = {} + # Konversi timestamp ke UTC datetime try: dt = self._convert_to_utc_datetime(timestamp) except Exception as e: _logger.warning(f"[Webhook] Gagal konversi waktu: {e}") dt = datetime.utcnow() + # Update tanggal driver 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: @@ -1843,18 +1865,37 @@ class StockPicking(models.Model): if shipping_status and self.shipping_status != shipping_status: updated_fields["shipping_status"] = shipping_status - # Log ke chatter + # Update field tambahan Biteship jika ada + if extra_data: + if extra_data.get("courier_driver_name"): + updated_fields["biteship_driver_name"] = extra_data["courier_driver_name"] + if extra_data.get("courier_driver_phone"): + updated_fields["biteship_driver_phone"] = extra_data["courier_driver_phone"] + if extra_data.get("courier_driver_plate_number"): + updated_fields["biteship_driver_plate_number"] = extra_data["courier_driver_plate_number"] + if extra_data.get("courier_link"): + updated_fields["biteship_courier_link"] = extra_data["courier_link"] + if extra_data.get("order_price"): + updated_fields["biteship_shipping_price"] = extra_data["order_price"] + if extra_data.get("status"): + updated_fields["biteship_shipping_status"] = extra_data["status"] + + # Format log untuk 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()}" + desc_clean = ' '.join(description.strip().split()) + log_line = f"[TRACKING] {status} - {dt_local.strftime('%d %b %Y %H:%M')}: {desc_clean}" + if not self._has_existing_log(log_line): - self.message_post(body=log_line) + self.with_user(15172).message_post(body=log_line) + # Apply update if updated_fields: self.write(updated_fields) + _logger.info(f"[Webhook] Updated fields on picking {self.name}: {updated_fields}") def _has_existing_log(self, log_line): -- cgit v1.2.3 From 6b780e717b8fe0b7959bd1a1f6d59b183d9845d9 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 21 Jun 2025 11:21:13 +0700 Subject: (andri) fix datetime log tracking --- indoteknik_custom/models/stock_picking.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f171c5d0..87363fd2 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1841,31 +1841,24 @@ class StockPicking(models.Model): def log_biteship_event_from_webhook(self, status, timestamp, description, extra_data=None): - """ - extra_data: dict opsional dari webhook (driver name, phone, plate, link, price, dsb) - """ self.ensure_one() updated_fields = {} - # Konversi timestamp ke UTC datetime try: dt = self._convert_to_utc_datetime(timestamp) except Exception as e: _logger.warning(f"[Webhook] Gagal konversi waktu: {e}") dt = datetime.utcnow() - # Update tanggal driver 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 - # Update field tambahan Biteship jika ada if extra_data: if extra_data.get("courier_driver_name"): updated_fields["biteship_driver_name"] = extra_data["courier_driver_name"] @@ -1880,19 +1873,20 @@ class StockPicking(models.Model): if extra_data.get("status"): updated_fields["biteship_shipping_status"] = extra_data["status"] - # Format log untuk chatter try: - dt_local = parser.parse(timestamp).replace(tzinfo=None) + dt_parsed = parser.parse(timestamp) + if dt_parsed.tzinfo is None: + dt_parsed = dt_parsed.replace(tzinfo=pytz.utc) + dt_local = dt_parsed.astimezone(pytz.timezone("Asia/Jakarta")) except Exception: - dt_local = dt + dt_local = dt.astimezone(pytz.timezone("Asia/Jakarta")) desc_clean = ' '.join(description.strip().split()) - log_line = f"[TRACKING] {status} - {dt_local.strftime('%d %b %Y %H:%M')}: {desc_clean}" + log_line = f"[TRACKING] {status} - {dt_local.strftime('%d %b %Y %H:%M')}:
{desc_clean}" if not self._has_existing_log(log_line): self.with_user(15172).message_post(body=log_line) - # Apply update if updated_fields: self.write(updated_fields) _logger.info(f"[Webhook] Updated fields on picking {self.name}: {updated_fields}") -- cgit v1.2.3 From 126774bd23276b80dd67dfb3bcdf3be633f6da86 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 21 Jun 2025 14:18:20 +0700 Subject: (andri) add user biteship live --- indoteknik_custom/models/stock_picking.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 87363fd2..7b5d98a2 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1885,7 +1885,8 @@ class StockPicking(models.Model): log_line = f"[TRACKING] {status} - {dt_local.strftime('%d %b %Y %H:%M')}:
{desc_clean}" if not self._has_existing_log(log_line): - self.with_user(15172).message_post(body=log_line) + self.with_user(15172).message_post(body=log_line) # user biteship test + # self.with_user(15710).message_post(body=log_line) # user biteship live if updated_fields: self.write(updated_fields) -- cgit v1.2.3 From 20f206f3d9b798fee50a06d4a462cf256a71d58e Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 21 Jun 2025 15:49:35 +0700 Subject: (andri) penambahan status tracking --- indoteknik_custom/models/stock_picking.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7b5d98a2..d4167609 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1820,9 +1820,7 @@ class StockPicking(models.Model): } 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", "") @@ -1831,9 +1829,14 @@ class StockPicking(models.Model): '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 {contact_name}', + 'return_in_transit': 'Pesanan dalam perjalanan kembali ke pengirim', + 'on_hold': 'Pesanan ditahan sementara karena masalah pengiriman', + 'rejected': 'Pesanan ditolak, silakan hubungi Biteship', + 'courier_not_found': 'Pesanan dibatalkan karena tidak ada kurir tersedia', + 'returned': 'Pesanan berhasil dikembalikan', + 'disposed': 'Pesanan sudah dimusnahkan', 'cancelled': 'Pesanan dibatalkan oleh sistem atau pengguna', } @@ -1941,10 +1944,15 @@ class StockPicking(models.Model): "allocated": "pending", "picking_up": "pending", "picked": "shipment", - "cancelled": "cancelled", - "on_hold": "on_hold", "dropping_off": "shipment", - "delivered": "completed" + "delivered": "completed", + "return_in_transit": "returning", + "on_hold": "on_hold", + "rejected": "cancelled", + "courier_not_found": "cancelled", + "returned": "returned", + "disposed": "disposed", + "cancelled": "cancelled" } return status_mapping.get(status, "Hubungi Admin") -- cgit v1.2.3 From af4ee6eef06a440207c6d16809d06e08057f66f0 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 20 Jun 2025 09:57:29 +0700 Subject: bu pick in purchase order sales match --- indoteknik_custom/models/stock_picking.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index eabef37c..7c4e6bc0 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1309,6 +1309,7 @@ class StockPicking(models.Model): self.final_seq = 0 self.set_picking_code_out() self.send_koli_to_so() + self.automatic_reserve_product() if (self.state_reserve == 'done' and self.picking_type_code == 'internal' and 'BU/PICK/' in self.name and self.linked_manual_bu_out): @@ -1347,6 +1348,9 @@ class StockPicking(models.Model): } self.send_mail_bills() return res + + # def automatic_reserve_product(self): + # if self.name def check_invoice_date(self): for picking in self: -- cgit v1.2.3 From a6aa700b5016c98d579a52125e3686acc615ce88 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 23 Jun 2025 16:29:02 +0700 Subject: trial automatic reserve and change qty purchase stock --- indoteknik_custom/models/stock_picking.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7c4e6bc0..27046063 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1309,8 +1309,6 @@ class StockPicking(models.Model): self.final_seq = 0 self.set_picking_code_out() self.send_koli_to_so() - self.automatic_reserve_product() - if (self.state_reserve == 'done' and self.picking_type_code == 'internal' and 'BU/PICK/' in self.name and self.linked_manual_bu_out): if not self.linked_manual_bu_out.date_reserved: @@ -1347,10 +1345,18 @@ class StockPicking(models.Model): 'target': 'new', } self.send_mail_bills() + if 'BU/PUT' in self.name: + self.automatic_reserve_product() return res - # def automatic_reserve_product(self): - # if self.name + def automatic_reserve_product(self): + if self.state == 'done': + po = self.env['purchase.order'].search([ + ('name', '=', self.group_id.name) + ]) + + for line in po.order_sales_match_line: + line.bu_pick.action_assign() def check_invoice_date(self): for picking in self: -- cgit v1.2.3 From 92bf91488eca67adabb6bfe8394385a9cee1032d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 25 Jun 2025 09:58:26 +0700 Subject: fix bu pick automatic reserve --- indoteknik_custom/models/stock_picking.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 27046063..c884f97e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1356,6 +1356,8 @@ class StockPicking(models.Model): ]) for line in po.order_sales_match_line: + if not line.bu_pick: + continue line.bu_pick.action_assign() def check_invoice_date(self): -- cgit v1.2.3 From 0e2fe03295f96560500c53ee61104a0a9f563576 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 27 Jun 2025 15:53:11 +0700 Subject: remove ask return in stock picking --- indoteknik_custom/models/stock_picking.py | 66 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 32 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6d868db4..1827c489 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1074,38 +1074,40 @@ class StockPicking(models.Model): self.approval_receipt_status = 'pengajuan1' def ask_return_approval(self): - for pick in self: - if self.env.user.is_accounting: - pick.approval_return_status = 'approved' - continue - else: - pick.approval_return_status = 'pengajuan1' - - action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_stock_return_note_wizard') - - if self.picking_type_code == 'outgoing': - if self.env.user.id in [3988, 3401, 20] or ( - self.env.user.has_group( - 'indoteknik_custom.group_role_purchasing') and 'Return of' in self.origin - ): - action['context'] = {'picking_ids': [x.id for x in self]} - return action - elif not self.env.user.has_group( - 'indoteknik_custom.group_role_purchasing') and 'Return of' in self.origin: - raise UserError('Harus Purchasing yang Ask Return') - else: - raise UserError('Harus Sales Admin yang Ask Return') - - elif self.picking_type_code == 'incoming': - if self.env.user.has_group('indoteknik_custom.group_role_purchasing') or ( - self.env.user.id in [3988, 3401, 20] and 'Return of' in self.origin - ): - action['context'] = {'picking_ids': [x.id for x in self]} - return action - elif not self.env.user.id in [3988, 3401, 20] and 'Return of' in self.origin: - raise UserError('Harus Sales Admin yang Ask Return') - else: - raise UserError('Harus Purchasing yang Ask Return') + pass + raise UserError("Bisa langsung Validate") + # for pick in self: + # if self.env.user.is_accounting: + # pick.approval_return_status = 'approved' + # continue + # else: + # pick.approval_return_status = 'pengajuan1' + # + # action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_stock_return_note_wizard') + # + # if self.picking_type_code == 'outgoing': + # if self.env.user.id in [3988, 3401, 20] or ( + # self.env.user.has_group( + # 'indoteknik_custom.group_role_purchasing') and 'Return of' in self.origin + # ): + # action['context'] = {'picking_ids': [x.id for x in self]} + # return action + # elif not self.env.user.has_group( + # 'indoteknik_custom.group_role_purchasing') and 'Return of' in self.origin: + # raise UserError('Harus Purchasing yang Ask Return') + # else: + # raise UserError('Harus Sales Admin yang Ask Return') + # + # elif self.picking_type_code == 'incoming': + # if self.env.user.has_group('indoteknik_custom.group_role_purchasing') or ( + # self.env.user.id in [3988, 3401, 20] and 'Return of' in self.origin + # ): + # action['context'] = {'picking_ids': [x.id for x in self]} + # return action + # elif not self.env.user.id in [3988, 3401, 20] and 'Return of' in self.origin: + # raise UserError('Harus Sales Admin yang Ask Return') + # else: + # raise UserError('Harus Purchasing yang Ask Return') def calculate_line_no(self): -- cgit v1.2.3 From 75fd0f87c6d1f8c3b92450f9826daa74550a5577 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 5 Jul 2025 15:03:29 +0700 Subject: (andri) info tambahan --- indoteknik_custom/models/stock_picking.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index d25208d1..e411aee6 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1773,6 +1773,7 @@ class StockPicking(models.Model): _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") return { 'error': str(e) } + # ACTION GET TRACKING MANUAL BITESHIP # def action_sync_biteship_tracking(self): # for picking in self: # if not picking.biteship_id: -- cgit v1.2.3 From a78f8184c2e7d45a65315eff0ea354996adb9cce Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 5 Jul 2025 15:54:17 +0700 Subject: (andri) info tambahan webhook --- indoteknik_custom/models/stock_picking.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index e411aee6..6e8c1067 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1866,6 +1866,7 @@ class StockPicking(models.Model): _logger.warning(f"[Webhook] Gagal konversi waktu: {e}") dt = datetime.utcnow() + # Penanganan status pengiriman 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: @@ -1875,7 +1876,9 @@ class StockPicking(models.Model): if shipping_status and self.shipping_status != shipping_status: updated_fields["shipping_status"] = shipping_status + # Penanganan extra data dari webhook if extra_data: + # Informasi kurir if extra_data.get("courier_driver_name"): updated_fields["biteship_driver_name"] = extra_data["courier_driver_name"] if extra_data.get("courier_driver_phone"): @@ -1884,11 +1887,21 @@ class StockPicking(models.Model): updated_fields["biteship_driver_plate_number"] = extra_data["courier_driver_plate_number"] if extra_data.get("courier_link"): updated_fields["biteship_courier_link"] = extra_data["courier_link"] + # Informasi harga if extra_data.get("order_price"): updated_fields["biteship_shipping_price"] = extra_data["order_price"] + # Status mentah dari Biteship if extra_data.get("status"): updated_fields["biteship_shipping_status"] = extra_data["status"] + # Tambahan untuk handle order.waybill_id + if extra_data.get("tracking_id"): + updated_fields["biteship_tracking_id"] = extra_data["tracking_id"] + updated_fields["delivery_tracking_no"] = extra_data["tracking_id"] + if extra_data.get("waybill_id"): + updated_fields["biteship_waybill_id"] = extra_data["waybill_id"] + + # Konversi waktu lokal untuk log try: dt_parsed = parser.parse(timestamp) if dt_parsed.tzinfo is None: @@ -1897,13 +1910,16 @@ class StockPicking(models.Model): except Exception: dt_local = dt.astimezone(pytz.timezone("Asia/Jakarta")) + # Format pesan log desc_clean = ' '.join(description.strip().split()) log_line = f"[TRACKING] {status} - {dt_local.strftime('%d %b %Y %H:%M')}:
{desc_clean}" + # Hindari log duplikat if not self._has_existing_log(log_line): - self.with_user(15172).message_post(body=log_line) # user biteship test - # self.with_user(15710).message_post(body=log_line) # user biteship live + self.with_user(15172).message_post(body=log_line) # Biteship user + # self.with_user(15710).message_post(body=log_line) # Biteship user live + # Update field-field terkait if updated_fields: self.write(updated_fields) _logger.info(f"[Webhook] Updated fields on picking {self.name}: {updated_fields}") -- cgit v1.2.3 From 6ff554e2dd9efb329b4d828881967413f8c641fd Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 5 Jul 2025 15:56:14 +0700 Subject: (andri) user biteship --- indoteknik_custom/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6e8c1067..33d21c8c 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1916,8 +1916,8 @@ class StockPicking(models.Model): # Hindari log duplikat if not self._has_existing_log(log_line): - self.with_user(15172).message_post(body=log_line) # Biteship user - # self.with_user(15710).message_post(body=log_line) # Biteship user live + # self.with_user(15172).message_post(body=log_line) # Biteship user + self.with_user(15710).message_post(body=log_line) # Biteship user live # Update field-field terkait if updated_fields: -- cgit v1.2.3 From 8837ae450af891898efd790e908d87664d2dd910 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 5 Jul 2025 16:47:26 +0700 Subject: (andri) fix user chatter biteship --- indoteknik_custom/models/stock_picking.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 33d21c8c..69718c7e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1916,8 +1916,12 @@ class StockPicking(models.Model): # Hindari log duplikat if not self._has_existing_log(log_line): - # self.with_user(15172).message_post(body=log_line) # Biteship user - self.with_user(15710).message_post(body=log_line) # Biteship user live + biteship_user = self.env['res.users'].sudo().browse(15710) # ID live + # biteship_user = self.env['res.users'].sudo().browse(15710) # ID user (cek di db) + self.sudo().message_post( + body=log_line, + author_id=biteship_user.partner_id.id + ) # Update field-field terkait if updated_fields: -- cgit v1.2.3 From 2af07f60a639089efc553966799b3dc225a397a2 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 14 Jul 2025 10:41:17 +0700 Subject: (andri) tracking & sync edit shipping method pada SO ke DO terkait --- indoteknik_custom/models/stock_picking.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 69718c7e..ccd42e94 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -119,7 +119,7 @@ class StockPicking(models.Model): waybill_id = fields.One2many(comodel_name='airway.bill', inverse_name='do_id', string='Airway Bill') purchase_representative_id = fields.Many2one('res.users', related='move_lines.purchase_line_id.order_id.user_id', string="Purchase Representative") - carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method') + carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method', tracking=3) shipping_status = fields.Char(string='Shipping Status', compute="_compute_shipping_status") date_reserved = fields.Datetime(string="Date Reserved", help='Tanggal ter-reserved semua barang nya', copy=False) status_printed = fields.Selection([ @@ -281,12 +281,12 @@ class StockPicking(models.Model): biteship_shipping_price = fields.Monetary('Biteship Shipping Price', currency_field='currency_id', help="Harga pengiriman dari Biteship") currency_id = fields.Many2one('res.currency', related='sale_id.currency_id', string='Currency', readonly=True) final_seq = fields.Float(string='Remaining Time') - shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id', help="Shipping Method yang digunakan di SO") - shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option', related='sale_id.shipping_option_id' , help="Shipping Option yang digunakan di SO") + shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id', help="Shipping Method yang digunakan di SO", tracking=3) + shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option', related='sale_id.shipping_option_id' , help="Shipping Option yang digunakan di SO", tracking=3) select_shipping_option_so = fields.Selection([ ('biteship', 'Biteship'), ('custom', 'Custom'), - ], string='Shipping Type', related='sale_id.select_shipping_option', help="Shipping Type yang digunakan di SO") + ], string='Shipping Type', related='sale_id.select_shipping_option', help="Shipping Type yang digunakan di SO", tracking=3) state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status') approval_invoice_date_id = fields.Many2one('approval.invoice.date', string='Approval Invoice Date') last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim', copy=False) -- cgit v1.2.3 From 27bd82c6e3e9ee8239751fffda02d4975ba23e68 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 14 Jul 2025 10:54:17 +0700 Subject: done --- indoteknik_custom/models/stock_picking.py | 100 ++++++++++++++++-------------- 1 file changed, 55 insertions(+), 45 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index ccd42e94..f7f854c7 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -20,8 +20,10 @@ _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" + + # biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" - + class StockPicking(models.Model): _inherit = 'stock.picking' @@ -278,16 +280,22 @@ class StockPicking(models.Model): biteship_driver_plate_number = fields.Char('Biteship Driver Plate Number') biteship_courier_link = fields.Char('Biteship Courier Link') biteship_shipping_status = fields.Char('Biteship Shipping Status', help="Status pengiriman dari Biteship") - biteship_shipping_price = fields.Monetary('Biteship Shipping Price', currency_field='currency_id', help="Harga pengiriman dari Biteship") + biteship_shipping_price = fields.Monetary('Biteship Shipping Price', currency_field='currency_id', + help="Harga pengiriman dari Biteship") currency_id = fields.Many2one('res.currency', related='sale_id.currency_id', string='Currency', readonly=True) final_seq = fields.Float(string='Remaining Time') - shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id', help="Shipping Method yang digunakan di SO", tracking=3) - shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option', related='sale_id.shipping_option_id' , help="Shipping Option yang digunakan di SO", tracking=3) + shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id', + help="Shipping Method yang digunakan di SO", tracking=3) + shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option', + related='sale_id.shipping_option_id', + help="Shipping Option yang digunakan di SO", tracking=3) select_shipping_option_so = fields.Selection([ ('biteship', 'Biteship'), ('custom', 'Custom'), - ], string='Shipping Type', related='sale_id.select_shipping_option', help="Shipping Type yang digunakan di SO", tracking=3) - state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status') + ], string='Shipping Type', related='sale_id.select_shipping_option', help="Shipping Type yang digunakan di SO", + tracking=3) + state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], + string='Packing Status') approval_invoice_date_id = fields.Many2one('approval.invoice.date', string='Approval Invoice Date') last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim', copy=False) update_date_doc_kirim_add = fields.Boolean(string='Update Tanggal Kirim Lewat ADD') @@ -298,7 +306,7 @@ class StockPicking(models.Model): if not self.name or not self.origin: return False return f"{self.name}" - + def _download_pod_photo(self, url): """Mengunduh foto POD dari URL""" try: @@ -307,7 +315,7 @@ class StockPicking(models.Model): return base64.b64encode(response.content) except Exception as e: raise UserError(f"Gagal mengunduh foto POD: {str(e)}") - + def _parse_datetime(self, dt_str): """Parse datetime string dari format KGX""" try: @@ -318,37 +326,37 @@ class StockPicking(models.Model): return datetime.strptime(dt_str, '%Y-%m-%dT%H:%M:%S') except ValueError: return False - + def action_get_kgx_pod(self, shipment=False): self.ensure_one() - + awb_number = shipment or self._get_kgx_awb_number() if not awb_number: raise UserError("Nomor AWB tidak dapat dibuat, pastikan picking memiliki name dan origin") - + url = "https://kgx.co.id/get_detail_awb" headers = {'Content-Type': 'application/json'} - payload = {"params" : {'awb_number': awb_number}} - + payload = {"params": {'awb_number': awb_number}} + try: response = requests.post(url, headers=headers, data=json.dumps(payload)) response.raise_for_status() data = response.json() - + if data.get('result', {}).get('data', []): pod_data = data['result']['data'][0].get('connote_pod', {}) photo_url = pod_data.get('photo') - + self.kgx_pod_photo_url = photo_url self.kgx_pod_signature = pod_data.get('signature') self.kgx_pod_receiver = pod_data.get('receiver') self.kgx_pod_receive_time = self._parse_datetime(pod_data.get('timeReceive')) self.driver_arrival_date = self._parse_datetime(pod_data.get('timeReceive')) - + return data else: raise UserError(f"Tidak ditemukan data untuk AWB: {awb_number}") - + except requests.exceptions.RequestException as e: raise UserError(f"Gagal mengambil data POD: {str(e)}") @@ -695,8 +703,9 @@ class StockPicking(models.Model): raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") if self.sale_id.select_shipping_option == 'custom': - raise UserError("Shipping Option pada Sales Order ini adalah *Custom*. Tidak dapat dikirim melalui Biteship.") - + raise UserError( + "Shipping Option pada Sales Order ini adalah *Custom*. Tidak dapat dikirim melalui Biteship.") + def is_courier_need_coordinates(service_code): return service_code in [ "instant", "same_day", "instant_car", @@ -793,11 +802,11 @@ class StockPicking(models.Model): self.message_post( body=f"Biteship berhasil dilakukan.
" - f"Kurir: {self.carrier_id.name}
" - f"Tracking ID: {self.biteship_tracking_id or '-'}
" - f"Resi: {waybill_id or '-'}
" - f"Reference: {self.name}
" - f"SO: {self.sale_id.name}", + f"Kurir: {self.carrier_id.name}
" + f"Tracking ID: {self.biteship_tracking_id or '-'}
" + f"Resi: {waybill_id or '-'}
" + f"Reference: {self.name}
" + f"SO: {self.sale_id.name}", message_type="comment" ) @@ -939,6 +948,9 @@ class StockPicking(models.Model): pending_section = None # Invoice values. invoice_vals = order._prepare_invoice() + invoice_date = self.date_done + invoice_vals['date'] = invoice_date + invoice_vals['invoice_date'] = invoice_date # Invoice line values (keep only necessary sections). for line in self.move_ids_without_package: po_line = self.env['purchase.order.line'].search( @@ -1304,7 +1316,6 @@ class StockPicking(models.Model): ) ) - self.validation_minus_onhand_quantity() self.responsible = self.env.user.id # self.send_koli_to_so() @@ -1355,7 +1366,7 @@ class StockPicking(models.Model): if 'BU/PUT' in self.name: self.automatic_reserve_product() return res - + def automatic_reserve_product(self): if self.state == 'done': po = self.env['purchase.order'].search([ @@ -1373,11 +1384,12 @@ class StockPicking(models.Model): continue invoice = self.env['account.move'].search( - [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel']), ('move_type', '=', 'out_invoice')], limit=1) + [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel']), + ('move_type', '=', 'out_invoice')], limit=1) if not invoice: continue - + if not picking.so_lama and invoice and (not picking.date_doc_kirim or not invoice.invoice_date): raise UserError("Tanggal Kirim atau Tanggal Invoice belum diisi!") @@ -1661,7 +1673,7 @@ class StockPicking(models.Model): self.ensure_one() order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) - + sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', order.name)], limit=1) product_shipped = [] @@ -1676,11 +1688,12 @@ class StockPicking(models.Model): 'delivery_order': { 'name': self.name, 'carrier': self.carrier_id.name or '-', - 'service' : order.delivery_service_type or '-', + 'service': order.delivery_service_type or '-', 'receiver_name': '', 'receiver_city': '' }, - 'delivered_date': self.driver_departure_date.strftime('%d %b %Y') if self.driver_departure_date != False else '-', + 'delivered_date': self.driver_departure_date.strftime( + '%d %b %Y') if self.driver_departure_date != False else '-', 'delivered': False, 'status': self.shipping_status, 'waybill_number': self.delivery_tracking_no or '-', @@ -1703,7 +1716,7 @@ class StockPicking(models.Model): elif sale_order_delay.status == 'early': day_start = day_start - sale_order_delay.days_delayed day_end = day_end - sale_order_delay.days_delayed - + eta_start = order.date_order + timedelta(days=day_start) eta_end = order.date_order + timedelta(days=day_end) formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" @@ -1732,7 +1745,7 @@ class StockPicking(models.Model): "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } - + manifests = [] try: @@ -1769,9 +1782,9 @@ class StockPicking(models.Model): "manifests": [], "delivered": False } - except Exception as e : + except Exception as e: _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") - return { 'error': str(e) } + return {'error': str(e)} # ACTION GET TRACKING MANUAL BITESHIP # def action_sync_biteship_tracking(self): @@ -1855,7 +1868,6 @@ class StockPicking(models.Model): return description_map.get(status, f"Status '{status}' diterima dari Biteship") - def log_biteship_event_from_webhook(self, status, timestamp, description, extra_data=None): self.ensure_one() updated_fields = {} @@ -1916,7 +1928,7 @@ class StockPicking(models.Model): # Hindari log duplikat if not self._has_existing_log(log_line): - biteship_user = self.env['res.users'].sudo().browse(15710) # ID live + biteship_user = self.env['res.users'].sudo().browse(15710) # ID live # biteship_user = self.env['res.users'].sudo().browse(15710) # ID user (cek di db) self.sudo().message_post( body=log_line, @@ -1928,7 +1940,6 @@ class StockPicking(models.Model): self.write(updated_fields) _logger.info(f"[Webhook] Updated fields on picking {self.name}: {updated_fields}") - def _has_existing_log(self, log_line): self.ensure_one() self.env.cr.execute(""" @@ -1995,8 +2006,7 @@ class StockPicking(models.Model): days_end = self.sale_id.estimated_arrival_days or (self.sale_id.estimated_arrival_days + 3) start_date = self.sale_id.create_date + datetime.timedelta(days=days_start) end_date = self.sale_id.create_date + datetime.timedelta(days=days_end) - - + add_day_start = 0 add_day_end = 0 sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', self.sale_id.name)], limit=1) @@ -2005,11 +2015,11 @@ class StockPicking(models.Model): add_day_start = sale_order_delay.days_delayed add_day_end = sale_order_delay.days_delayed elif sale_order_delay.status == 'early': - add_day_start = -abs(sale_order_delay.days_delayed) - add_day_end = -abs(sale_order_delay.days_delayed) - - fastest_eta = start_date +datetime.timedelta(days=add_day_start + add_day_start) - + add_day_start = -abs(sale_order_delay.days_delayed) + add_day_end = -abs(sale_order_delay.days_delayed) + + fastest_eta = start_date + datetime.timedelta(days=add_day_start + add_day_start) + longest_eta = end_date + datetime.timedelta(days=add_day_end) format_time = '%d %b %Y' -- cgit v1.2.3 From ee77ca5ec2947ba672c7becee3ecb583f4f25916 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 14 Jul 2025 17:02:45 +0700 Subject: deactivate code change name bu/in --- indoteknik_custom/models/stock_picking.py | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 69718c7e..6f260dfa 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1563,25 +1563,25 @@ class StockPicking(models.Model): new_picking.state_packing = 'packing_done' self._use_faktur(vals) self.sync_sale_line(vals) - for picking in self: - # Periksa apakah kondisi terpenuhi saat data diubah - if (vals.get('picking_type_code', picking.picking_type_code) == 'incoming' and - vals.get('location_dest_id', picking.location_dest_id.id) == 58): - if 'name' in vals or picking.name.startswith('BU/IN/'): - name_to_modify = vals.get('name', picking.name) - if name_to_modify.startswith('BU/IN/'): - vals['name'] = name_to_modify.replace('BU/IN/', 'BU/INPUT/', 1) - - if (vals.get('picking_type_code', picking.picking_type_code) == 'internal' and - vals.get('location_id', picking.location_id.id) == 58): - name_to_modify = vals.get('name', picking.name) - if name_to_modify.startswith('BU/INT'): - new_name = name_to_modify.replace('BU/INT', 'BU/IN', 1) - # Periksa apakah nama sudah ada - if self.env['stock.picking'].search_count( - [('name', '=', new_name), ('company_id', '=', picking.company_id.id)]) > 0: - new_name = f"{new_name}-DUP" - vals['name'] = new_name + # for picking in self: + # # Periksa apakah kondisi terpenuhi saat data diubah + # if (vals.get('picking_type_code', picking.picking_type_code) == 'incoming' and + # vals.get('location_dest_id', picking.location_dest_id.id) == 58): + # if 'name' in vals or picking.name.startswith('BU/IN/'): + # name_to_modify = vals.get('name', picking.name) + # if name_to_modify.startswith('BU/IN/'): + # vals['name'] = name_to_modify.replace('BU/IN/', 'BU/INPUT/', 1) + + # if (vals.get('picking_type_code', picking.picking_type_code) == 'internal' and + # vals.get('location_id', picking.location_id.id) == 58): + # name_to_modify = vals.get('name', picking.name) + # if name_to_modify.startswith('BU/INT'): + # new_name = name_to_modify.replace('BU/INT', 'BU/IN', 1) + # # Periksa apakah nama sudah ada + # if self.env['stock.picking'].search_count( + # [('name', '=', new_name), ('company_id', '=', picking.company_id.id)]) > 0: + # new_name = f"{new_name}-DUP" + # vals['name'] = new_name return super(StockPicking, self).write(vals) def _use_faktur(self, vals): -- cgit v1.2.3 From 7f4510f2459e16b7b3f1828f736ec84bc7ea97dc Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 15 Jul 2025 08:53:29 +0700 Subject: fix bug api kgx --- indoteknik_custom/models/stock_picking.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index b9c90551..0efffd2f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -320,13 +320,18 @@ class StockPicking(models.Model): """Parse datetime string dari format KGX""" try: from datetime import datetime - # Hilangkan timezone jika ada masalah parsing + + if not dt_str: + return False + if '+' in dt_str: dt_str = dt_str.split('+')[0] + return datetime.strptime(dt_str, '%Y-%m-%dT%H:%M:%S') except ValueError: return False + def action_get_kgx_pod(self, shipment=False): self.ensure_one() -- cgit v1.2.3 From 77d3bd2541a52270f03b84e38dd691630bcd82af Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 08:47:04 +0700 Subject: mapping koli tukar guling --- indoteknik_custom/models/stock_picking.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indoteknik_custom/models/stock_picking.py') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7d3d963a..dc5b8ebd 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -2518,6 +2518,8 @@ class KonfirmKoli(models.Model): copy=False, ) pick_id = fields.Many2one('stock.picking', string='Pick') + product_id = fields.Many2one('product.product', string='Product') + qty_done = fields.Float(string='Qty Done') @api.constrains('pick_id') def _check_duplicate_pick_id(self): -- cgit v1.2.3