From dbe24b9cd600c7b5a9d0587f80a782ed93c9a761 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 2 Dec 2024 13:37:28 +0700 Subject: add name to validation --- 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 17dd5766..03b10cdb 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -539,7 +539,7 @@ class StockPicking(models.Model): qty_onhand = check_qty_per_inventory(self, line.product_id, line.location_id) if line.qty_done > qty_onhand: - raise UserError('Quantity Done melebihi Quantity Onhand') + raise UserError(f'{line.product_id.display_name} : Quantity Done melebihi Quantity Onhand') def button_validate(self): if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': -- cgit v1.2.3 From 75fa42b4f5afb7a70e30c84b7a348f65e1bb99e9 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 4 Dec 2024 14:08:32 +0700 Subject: api tracking envio --- indoteknik_custom/models/stock_picking.py | 115 +++++++++++++++++++++++++++++- 1 file changed, 112 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 03b10cdb..0d8c6b0f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,9 +1,11 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tools.float_utils import float_is_zero -from datetime import datetime +from datetime import datetime, timedelta from itertools import groupby -import pytz, datetime, requests, json +import pytz, requests, json, requests +from dateutil import parser +# import datetime class StockPicking(models.Model): @@ -113,6 +115,113 @@ class StockPicking(models.Model): ], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.") notee = fields.Text(string="Note") + # Envio Tracking Section + envio_id = fields.Char(string="Envio ID", readonly=True) + envio_code = fields.Char(string="Envio Code", readonly=True) + envio_ref_code = fields.Char(string="Envio Reference Code", readonly=True) + envio_eta_at = fields.Char(string="Estimated Time of Arrival (ETA)", readonly=True) + envio_ata_at = fields.Char(string="Actual Time of Arrival (ATA)", readonly=True) + envio_etd_at = fields.Char(string="Estimated Time of Departure (ETD)", readonly=True) + envio_atd_at = fields.Char(string="Actual Time of Departure (ATD)", readonly=True) + envio_received_by = fields.Char(string="Received By", readonly=True) + envio_status = fields.Char(string="Status", readonly=True) + envio_cod_value = fields.Float(string="COD Value", readonly=True) + envio_cod_status = fields.Char(string="COD Status", readonly=True) + envio_logs = fields.Text(string="Logs", readonly=True) + envio_latest_message = fields.Text(string="Latest Log Message", readonly=True) + envio_latest_recorded_at = fields.Char(string="Log Recorded At", readonly=True) + envio_latest_latitude = fields.Float(string="Log Latitude", readonly=True) + envio_latest_longitude = fields.Float(string="Log Longitude", readonly=True) + tracking_by = fields.Many2one('res.users', string='Tracking By', readonly=True, tracking=True) + + def _convert_to_wib(self, date_str): + """ + Mengonversi string waktu ISO 8601 ke format waktu Indonesia (WIB) + """ + if not date_str: + return False + try: + utc_time = datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') + wib_time = utc_time + timedelta(hours=7) + return wib_time.strftime('%d-%m-%Y %H:%M:%S') + except ValueError: + raise UserError(f"Format waktu tidak sesuai: {date_str}") + + def _convert_to_datetime(self, date_str): + """Mengonversi string waktu dari API ke datetime.""" + if not date_str: + return False + try: + # Format waktu dengan milidetik + return datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S.%fZ') + except ValueError: + try: + # Format waktu tanpa milidetik + return datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') + except ValueError: + raise UserError(f"Format waktu tidak sesuai: {date_str}") + + def track_envio_shipment(self): + pickings = self.env['stock.picking'].search([ + ('picking_type_code', '=', 'outgoing'), + ('state', '=', 'done'), + ('carrier_id', '=', 151) + ]) + for picking in pickings: + 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}" + headers = { + 'Authorization': 'Bearer JZ0Seh6qpYJAC3CJHdhF7sPqv8B/uSSfZe1VX5BL?vPYdo', + 'Content-Type': 'application/json', + } + + try: + # Request ke API + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() # Raise error jika status code bukan 200 + response_data = response.json() + + # Validasi jika respons tidak sesuai format yang diharapkan + if not response_data or "data" not in response_data: + raise UserError("Respons API tidak sesuai format yang diharapkan.") + + data = response_data.get("data") + if not data: + continue + + # Menyimpan data ke field masing-masing + picking.envio_id = data.get("id") + picking.envio_code = data.get("code") + picking.envio_ref_code = data.get("ref_code") + picking.envio_eta_at = self._convert_to_datetime(data.get("eta_at")) + picking.envio_ata_at = self._convert_to_datetime(data.get("ata_at")) + picking.envio_etd_at = self._convert_to_datetime(data.get("etd_at")) + picking.envio_atd_at = self._convert_to_datetime(data.get("atd_at")) + picking.envio_received_by = data.get("received_by") + picking.envio_status = data.get("status") + picking.envio_cod_value = data.get("cod_value", 0.0) + picking.envio_cod_status = data.get("cod_status") + + # Menyimpan log terbaru + logs = data.get("logs", []) + if logs and isinstance(logs, list) and logs[0]: + latest_log = logs[0] + picking.envio_latest_message = latest_log.get("message", "Log kosong.") + picking.envio_latest_recorded_at = self._convert_to_datetime(latest_log.get("recorded_at")) + picking.envio_latest_latitude = latest_log.get("latitude", 0.0) + picking.envio_latest_longitude = latest_log.get("longitude", 0.0) + + picking.tracking_by = self.env.user.id + ata_at_str = data.get("ata_at") + picking.driver_arrival_date = self._convert_to_datetime(ata_at_str) if ata_at_str else False + except requests.exceptions.RequestException as e: + raise UserError(f"Terjadi kesalahan saat menghubungi API Envio: {str(e)}") + except Exception as e: + raise UserError(f"Kesalahan tidak terduga: {str(e)}") + def action_send_to_biteship(self): url = "https://api.biteship.com/v1/orders" api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" @@ -539,7 +648,7 @@ class StockPicking(models.Model): qty_onhand = check_qty_per_inventory(self, line.product_id, line.location_id) if line.qty_done > qty_onhand: - raise UserError(f'{line.product_id.display_name} : Quantity Done melebihi Quantity Onhand') + raise UserError('Quantity Done melebihi Quantity Onhand') def button_validate(self): if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': -- cgit v1.2.3 From 61cfbef8ec24ca67c9a65b7901bb9ec615364977 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 4 Dec 2024 14:20:40 +0700 Subject: fix error --- 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 0d8c6b0f..0da258e5 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta from itertools import groupby import pytz, requests, json, requests from dateutil import parser -# import datetime +import datetime class StockPicking(models.Model): -- cgit v1.2.3 From 65a1d7c8fb4e4bd3b055efec5362d98a2d4340fc Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 4 Dec 2024 14:27:53 +0700 Subject: test api envio --- 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 0da258e5..327a4223 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -167,7 +167,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 4eac4d0709ec5d9e03b517a39cb67acbc35e2932 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 4 Dec 2024 14:43:55 +0700 Subject: fix bug --- 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 327a4223..931da588 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,7 +1,7 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tools.float_utils import float_is_zero -from datetime import datetime, timedelta +from datetime import timedelta from itertools import groupby import pytz, requests, json, requests from dateutil import parser -- cgit v1.2.3 From 32a33b7ef6c61424bc9785bdb4246a9be58e5b42 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 5 Dec 2024 16:10:09 +0700 Subject: fix bug api envio --- indoteknik_custom/models/stock_picking.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 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 931da588..6d911470 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,7 +1,7 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tools.float_utils import float_is_zero -from datetime import timedelta +from datetime import timedelta, datetime from itertools import groupby import pytz, requests, json, requests from dateutil import parser @@ -119,17 +119,17 @@ class StockPicking(models.Model): envio_id = fields.Char(string="Envio ID", readonly=True) envio_code = fields.Char(string="Envio Code", readonly=True) envio_ref_code = fields.Char(string="Envio Reference Code", readonly=True) - envio_eta_at = fields.Char(string="Estimated Time of Arrival (ETA)", readonly=True) - envio_ata_at = fields.Char(string="Actual Time of Arrival (ATA)", readonly=True) - envio_etd_at = fields.Char(string="Estimated Time of Departure (ETD)", readonly=True) - envio_atd_at = fields.Char(string="Actual Time of Departure (ATD)", readonly=True) + envio_eta_at = fields.Datetime(string="Estimated Time of Arrival (ETA)", readonly=True) + envio_ata_at = fields.Datetime(string="Actual Time of Arrival (ATA)", readonly=True) + envio_etd_at = fields.Datetime(string="Estimated Time of Departure (ETD)", readonly=True) + envio_atd_at = fields.Datetime(string="Actual Time of Departure (ATD)", readonly=True) envio_received_by = fields.Char(string="Received By", readonly=True) envio_status = fields.Char(string="Status", readonly=True) envio_cod_value = fields.Float(string="COD Value", readonly=True) envio_cod_status = fields.Char(string="COD Status", readonly=True) envio_logs = fields.Text(string="Logs", readonly=True) envio_latest_message = fields.Text(string="Latest Log Message", readonly=True) - envio_latest_recorded_at = fields.Char(string="Log Recorded At", readonly=True) + envio_latest_recorded_at = fields.Datetime(string="Log Recorded At", readonly=True) envio_latest_latitude = fields.Float(string="Log Latitude", readonly=True) envio_latest_longitude = fields.Float(string="Log Longitude", readonly=True) tracking_by = fields.Many2one('res.users', string='Tracking By', readonly=True, tracking=True) @@ -141,7 +141,7 @@ class StockPicking(models.Model): if not date_str: return False try: - utc_time = datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') + utc_time = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') wib_time = utc_time + timedelta(hours=7) return wib_time.strftime('%d-%m-%Y %H:%M:%S') except ValueError: @@ -153,11 +153,13 @@ class StockPicking(models.Model): return False try: # Format waktu dengan milidetik - return datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S.%fZ') + date = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S.%fZ') + return date except ValueError: try: # Format waktu tanpa milidetik - return datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') + date = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') + return date except ValueError: raise UserError(f"Format waktu tidak sesuai: {date_str}") @@ -167,7 +169,7 @@ class StockPicking(models.Model): ('state', '=', 'done'), ('carrier_id', '=', 151) ]) - for picking in self: + for picking in pickings: if not picking.name: raise UserError("Name pada stock.picking tidak ditemukan.") @@ -216,7 +218,12 @@ class StockPicking(models.Model): picking.tracking_by = self.env.user.id ata_at_str = data.get("ata_at") - picking.driver_arrival_date = self._convert_to_datetime(ata_at_str) if ata_at_str else False + envio_ata = self._convert_to_datetime(data.get("ata_at")) + + picking.driver_arrival_date = envio_ata + if data.get("status") == 'returned': + picking.driver_arrival_date = False + picking.envio_ata_at = False except requests.exceptions.RequestException as e: raise UserError(f"Terjadi kesalahan saat menghubungi API Envio: {str(e)}") except Exception as e: -- cgit v1.2.3 From 3771d33ddb1abf2ffe434d71bd1dbeadcb83c00d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 6 Dec 2024 09:01:32 +0700 Subject: fix bug --- indoteknik_custom/models/stock_picking.py | 69 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 36 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 6d911470..37e43054 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -169,11 +169,11 @@ class StockPicking(models.Model): ('state', '=', 'done'), ('carrier_id', '=', 151) ]) + for picking in pickings: 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}" headers = { 'Authorization': 'Bearer JZ0Seh6qpYJAC3CJHdhF7sPqv8B/uSSfZe1VX5BL?vPYdo', @@ -181,53 +181,50 @@ class StockPicking(models.Model): } try: - # Request ke API response = requests.get(url, headers=headers, timeout=10) - response.raise_for_status() # Raise error jika status code bukan 200 + response.raise_for_status() response_data = response.json() - # Validasi jika respons tidak sesuai format yang diharapkan - if not response_data or "data" not in response_data: - raise UserError("Respons API tidak sesuai format yang diharapkan.") - data = response_data.get("data") if not data: continue - # Menyimpan data ke field masing-masing - picking.envio_id = data.get("id") - picking.envio_code = data.get("code") - picking.envio_ref_code = data.get("ref_code") - picking.envio_eta_at = self._convert_to_datetime(data.get("eta_at")) - picking.envio_ata_at = self._convert_to_datetime(data.get("ata_at")) - picking.envio_etd_at = self._convert_to_datetime(data.get("etd_at")) - picking.envio_atd_at = self._convert_to_datetime(data.get("atd_at")) - picking.envio_received_by = data.get("received_by") - picking.envio_status = data.get("status") - picking.envio_cod_value = data.get("cod_value", 0.0) - picking.envio_cod_status = data.get("cod_status") - - # Menyimpan log terbaru - logs = data.get("logs", []) - if logs and isinstance(logs, list) and logs[0]: - latest_log = logs[0] - picking.envio_latest_message = latest_log.get("message", "Log kosong.") - picking.envio_latest_recorded_at = self._convert_to_datetime(latest_log.get("recorded_at")) - picking.envio_latest_latitude = latest_log.get("latitude", 0.0) - picking.envio_latest_longitude = latest_log.get("longitude", 0.0) - + # Mapping field dengan data API + fields_map = { + 'envio_id': "id", + 'envio_code': "code", + 'envio_ref_code': "ref_code", + 'envio_eta_at': "eta_at", + 'envio_ata_at': "ata_at", + 'envio_etd_at': "etd_at", + 'envio_atd_at': "atd_at", + 'envio_received_by': "received_by", + 'envio_status': "status", + 'envio_cod_value': "cod_value", + 'envio_cod_status': "cod_status", + } + + for field, key in fields_map.items(): + setattr(picking, field, data.get(key, 0.0 if 'cod' in key else False)) + + # Menyimpan log terbaru jika ada + latest_log = (data.get("logs") or [{}])[0] + picking.envio_latest_message = latest_log.get("message", "Log kosong.") + picking.envio_latest_recorded_at = self._convert_to_datetime(latest_log.get("recorded_at")) + picking.envio_latest_latitude = latest_log.get("latitude", 0.0) + picking.envio_latest_longitude = latest_log.get("longitude", 0.0) + + # Menyimpan data tambahan picking.tracking_by = self.env.user.id - ata_at_str = data.get("ata_at") envio_ata = self._convert_to_datetime(data.get("ata_at")) + picking.driver_arrival_date = envio_ata if envio_ata != '0001-01-01 00:00:00' else False + picking.envio_ata_at = envio_ata if envio_ata != '0001-01-01 00:00:00' else False - picking.driver_arrival_date = envio_ata - if data.get("status") == 'returned': - picking.driver_arrival_date = False - picking.envio_ata_at = False except requests.exceptions.RequestException as e: - raise UserError(f"Terjadi kesalahan saat menghubungi API Envio: {str(e)}") + raise UserError(f"Terjadi kesalahan saat menghubungi API Envio: {e}") except Exception as e: - raise UserError(f"Kesalahan tidak terduga: {str(e)}") + raise UserError(f"Kesalahan tidak terduga: {e}") + def action_send_to_biteship(self): url = "https://api.biteship.com/v1/orders" -- cgit v1.2.3 From b0e71c613a80283a76371a468d54771b284205b5 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 6 Dec 2024 11:27:03 +0700 Subject: fix bug envio --- indoteknik_custom/models/stock_picking.py | 69 ++++++++++++++++--------------- 1 file changed, 36 insertions(+), 33 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 37e43054..e6506a0b 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -169,11 +169,11 @@ class StockPicking(models.Model): ('state', '=', 'done'), ('carrier_id', '=', 151) ]) - for picking in pickings: 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}" headers = { 'Authorization': 'Bearer JZ0Seh6qpYJAC3CJHdhF7sPqv8B/uSSfZe1VX5BL?vPYdo', @@ -181,50 +181,53 @@ class StockPicking(models.Model): } try: + # Request ke API response = requests.get(url, headers=headers, timeout=10) - response.raise_for_status() + response.raise_for_status() # Raise error jika status code bukan 200 response_data = response.json() + # Validasi jika respons tidak sesuai format yang diharapkan + if not response_data or "data" not in response_data: + raise UserError("Respons API tidak sesuai format yang diharapkan.") + data = response_data.get("data") if not data: continue - # Mapping field dengan data API - fields_map = { - 'envio_id': "id", - 'envio_code': "code", - 'envio_ref_code': "ref_code", - 'envio_eta_at': "eta_at", - 'envio_ata_at': "ata_at", - 'envio_etd_at': "etd_at", - 'envio_atd_at': "atd_at", - 'envio_received_by': "received_by", - 'envio_status': "status", - 'envio_cod_value': "cod_value", - 'envio_cod_status': "cod_status", - } - - for field, key in fields_map.items(): - setattr(picking, field, data.get(key, 0.0 if 'cod' in key else False)) - - # Menyimpan log terbaru jika ada - latest_log = (data.get("logs") or [{}])[0] - picking.envio_latest_message = latest_log.get("message", "Log kosong.") - picking.envio_latest_recorded_at = self._convert_to_datetime(latest_log.get("recorded_at")) - picking.envio_latest_latitude = latest_log.get("latitude", 0.0) - picking.envio_latest_longitude = latest_log.get("longitude", 0.0) - - # Menyimpan data tambahan + # Menyimpan data ke field masing-masing + picking.envio_id = data.get("id") + picking.envio_code = data.get("code") + picking.envio_ref_code = data.get("ref_code") + picking.envio_eta_at = self._convert_to_datetime(data.get("eta_at")) + picking.envio_ata_at = self._convert_to_datetime(data.get("ata_at")) + picking.envio_etd_at = self._convert_to_datetime(data.get("etd_at")) + picking.envio_atd_at = self._convert_to_datetime(data.get("atd_at")) + picking.envio_received_by = data.get("received_by") + picking.envio_status = data.get("status") + picking.envio_cod_value = data.get("cod_value", 0.0) + picking.envio_cod_status = data.get("cod_status") + + # Menyimpan log terbaru + logs = data.get("logs", []) + if logs and isinstance(logs, list) and logs[0]: + latest_log = logs[0] + picking.envio_latest_message = latest_log.get("message", "Log kosong.") + picking.envio_latest_recorded_at = self._convert_to_datetime(latest_log.get("recorded_at")) + picking.envio_latest_latitude = latest_log.get("latitude", 0.0) + picking.envio_latest_longitude = latest_log.get("longitude", 0.0) + picking.tracking_by = self.env.user.id + ata_at_str = data.get("ata_at") envio_ata = self._convert_to_datetime(data.get("ata_at")) - picking.driver_arrival_date = envio_ata if envio_ata != '0001-01-01 00:00:00' else False - picking.envio_ata_at = envio_ata if envio_ata != '0001-01-01 00:00:00' else False + picking.driver_arrival_date = envio_ata + if data.get("status") != 'delivered': + picking.driver_arrival_date = False + picking.envio_ata_at = False except requests.exceptions.RequestException as e: - raise UserError(f"Terjadi kesalahan saat menghubungi API Envio: {e}") + raise UserError(f"Terjadi kesalahan saat menghubungi API Envio: {str(e)}") except Exception as e: - raise UserError(f"Kesalahan tidak terduga: {e}") - + raise UserError(f"Kesalahan tidak terduga: {str(e)}") def action_send_to_biteship(self): url = "https://api.biteship.com/v1/orders" -- cgit v1.2.3 From 5343cbd0bfceb6bd50dbbed55a1de4b116f5fef4 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 13 Dec 2024 15:53:20 +0700 Subject: update code --- indoteknik_custom/models/stock_picking.py | 13 +++++++++++++ 1 file changed, 13 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 e6506a0b..e25704f4 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -115,6 +115,19 @@ class StockPicking(models.Model): ], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.") notee = fields.Text(string="Note") + @api.model + def _compute_dokumen_tanda_terima(self): + for picking in self: + picking.dokumen_tanda_terima = picking.partner_id.dokumen_pengiriman + + @api.model + def _compute_dokumen_pengiriman(self): + for picking in self: + picking.dokumen_pengiriman = picking.partner_id.dokumen_pengiriman_input + + dokumen_tanda_terima = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang', readonly=True, compute=_compute_dokumen_tanda_terima) + dokumen_pengiriman = fields.Char(string='Dokumen yang Dibawa Saat Pengiriman Barang', readonly=True, compute=_compute_dokumen_pengiriman) + # Envio Tracking Section envio_id = fields.Char(string="Envio ID", readonly=True) envio_code = fields.Char(string="Envio Code", readonly=True) -- cgit v1.2.3 From 02d27a0e871dd4949c9382000843cd35dd3db3f8 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 8 Jan 2025 09:48:07 +0700 Subject: automatic email bill, api lalamove --- indoteknik_custom/models/stock_picking.py | 195 ++++++++++++++++++++++++++++-- 1 file changed, 183 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 e6506a0b..51899de9 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -6,10 +6,17 @@ from itertools import groupby import pytz, requests, json, requests from dateutil import parser import datetime - +import hmac +import hashlib +import base64 +import requests +import time +import logging +_logger = logging.getLogger(__name__) class StockPicking(models.Model): _inherit = 'stock.picking' + # check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') account_id = fields.Many2one('account.account', string='Account') efaktur_id = fields.Many2one('vit.efaktur', string='Faktur Pajak') @@ -134,6 +141,86 @@ class StockPicking(models.Model): envio_latest_longitude = fields.Float(string="Log Longitude", readonly=True) tracking_by = fields.Many2one('res.users', string='Tracking By', readonly=True, tracking=True) + # Lalamove Section + lalamove_order_id = fields.Char(string="Lalamove Order ID", copy=False) + lalamove_address = fields.Char(string="Lalamove Address") + lalamove_name = fields.Char(string="Lalamove Name") + lalamove_phone = fields.Char(string="Lalamove Phone") + lalamove_status = fields.Char(string="Lalamove Status") + lalamove_delivered_at = fields.Datetime(string="Lalamove Delivered At") + lalamove_data = fields.Text(string="Lalamove Data", readonly=True) + lalamove_image_url = fields.Char(string="Lalamove Image URL") + lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") + + def _compute_lalamove_image_html(self): + for record in self: + if record.lalamove_image_url: + record.lalamove_image_html = f'' + else: + record.lalamove_image_html = "No image available." + + def action_fetch_lalamove_order(self): + pickings = self.env['stock.picking'].search([ + ('picking_type_code', '=', 'outgoing'), + ('state', '=', 'done'), + ('carrier_id', '=', 9) + ]) + for picking in pickings: + try: + order_id = picking.lalamove_order_id + apikey = self.env['ir.config_parameter'].sudo().get_param('lalamove.apikey') + secret = self.env['ir.config_parameter'].sudo().get_param('lalamove.secret') + market = self.env['ir.config_parameter'].sudo().get_param('lalamove.market', default='ID') + + order_data = picking.get_lalamove_order(order_id, apikey, secret, market) + picking.lalamove_data = order_data + except Exception as e: + _logger.error(f"Error fetching Lalamove order for picking {picking.id}: {str(e)}") + continue + + def get_lalamove_order(self, order_id, apikey, secret, market): + timestamp = str(int(time.time() * 1000)) + message = f"{timestamp}\r\nGET\r\n/v3/orders/{order_id}\r\n\r\n" + signature = hmac.new(secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest() + + headers = { + "Content-Type": "application/json", + "Authorization": f"hmac {apikey}:{timestamp}:{signature}", + "Market": market + } + + url = f"https://rest.lalamove.com/v3/orders/{order_id}" + response = requests.get(url, headers=headers) + + if response.status_code == 200: + data = response.json() + stops = data.get("data", {}).get("stops", []) + + for stop in stops: + pod = stop.get("POD", {}) + if pod.get("status") == "DELIVERED": + image_url = pod.get("image") # Sesuaikan jika key berbeda + self.lalamove_image_url = image_url + + address = stop.get("address") + name = stop.get("name") + phone = stop.get("phone") + delivered_at = pod.get("deliveredAt") + + delivered_at_dt = self._convert_to_datetime(delivered_at) + + self.lalamove_address = address + self.lalamove_name = name + self.lalamove_phone = phone + self.lalamove_status = pod.get("status") + self.lalamove_delivered_at = delivered_at_dt + return data + + raise UserError("No delivered data found in Lalamove response.") + else: + raise UserError(f"Error {response.status_code}: {response.text}") + + def _convert_to_wib(self, date_str): """ Mengonversi string waktu ISO 8601 ke format waktu Indonesia (WIB) @@ -661,13 +748,13 @@ class StockPicking(models.Model): if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': if self.origin and 'Return of' in self.origin: raise UserError("Button ini hanya untuk Logistik") - + if self.picking_type_code == 'internal': self.check_qty_done_stock() if self._name != 'stock.picking': return super(StockPicking, self).button_validate() - + if not self.picking_code: self.picking_code = self.env['ir.sequence'].next_by_code('stock.picking.code') or '0' @@ -681,15 +768,10 @@ class StockPicking(models.Model): raise UserError("Harus di Approve oleh Accounting") if self.picking_type_id.id == 28 and not self.env.user.is_logistic_approver: - raise UserError("Harus di Approve oleh Logistik") + raise UserError("Harus di Approve oleh Logistik") if self.location_dest_id.id == 47 and not self.env.user.is_purchasing_manager: - raise UserError("Transfer ke gudang selisih harus di approve Rafly Hanggara") - - # if self.group_id.sale_id: - # if self.group_id.sale_id.payment_link_midtrans: - # if self.group_id.sale_id.payment_status != 'settlement' and self.group_id.sale_id.state == 'draft': - # raise UserError('Uang belum masuk (settlement), mohon konfirmasi ke sales atau finance') + raise UserError("Transfer ke gudang selisih harus di approve Rafly Hanggara") if self.is_internal_use: self.approval_status = 'approved' @@ -707,14 +789,57 @@ class StockPicking(models.Model): if not self.date_reserved: current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.date_reserved = current_time - + self.validation_minus_onhand_quantity() self.responsible = self.env.user.id res = super(StockPicking, self).button_validate() self.calculate_line_no() self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' + + template = self.env.ref('indoteknik_custom.mail_template_invoice_po_document') + if template and self.purchase_id: + # Render email body + email_values = template.sudo().generate_email( + res_ids=[self.purchase_id.id], + fields=['body_html'] + ) + rendered_body = email_values.get(self.purchase_id.id, {}).get('body_html', '') + + # Render report dengan XML ID + report = self.env.ref('purchase.action_report_purchase_order') # Gunakan XML ID laporan + if not report: + raise UserError("Laporan dengan XML ID 'purchase.action_report_purchase_order' tidak ditemukan.") + + # Render laporan ke PDF + pdf_content, _ = report._render_qweb_pdf([self.purchase_id.id]) + report_content = base64.b64encode(pdf_content).decode('utf-8') + + # Kirim email menggunakan template + email_sent = template.sudo().send_mail(self.purchase_id.id, force_send=True) + + if email_sent: + # Buat attachment untuk laporan + attachment = self.env['ir.attachment'].create({ + 'name': self.purchase_id.name or "Laporan Invoice.pdf", + 'type': 'binary', + 'datas': report_content, + 'res_model': 'purchase.order', + 'res_id': self.purchase_id.id, + 'mimetype': 'application/pdf', + }) + + # Tambahkan isi email dan laporan ke log note + self.purchase_id.message_post( + body=rendered_body, + subject="Pengiriman Email Invoice", + message_type='comment', + subtype_xmlid="mail.mt_note", + attachment_ids=[attachment.id], + ) + return res + def action_cancel(self): if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': if self.origin and 'Return of' in self.origin: @@ -858,4 +983,50 @@ class StockPicking(models.Model): formatted_fastest_eta = fastest_eta.strftime(format_time_fastest) formatted_longest_eta = longest_eta.strftime(format_time) - return f'{formatted_fastest_eta} - {formatted_longest_eta}' \ No newline at end of file + return f'{formatted_fastest_eta} - {formatted_longest_eta}' + +# class CheckProduct(models.Model): +# _name = 'check.product' +# _description = 'Check Product' +# _order = 'picking_id, id' + +# picking_id = fields.Many2one('stock.picking', string='Picking Reference', required=True, ondelete='cascade', index=True, copy=False) +# product_id = fields.Many2one('product.product', string='Product') + + +# @api.constrains('product_id') +# def check_product_validity(self): +# """ +# Validate if the product exists in the related stock.picking's move_ids_without_package +# and ensure that the product's quantity does not exceed the available product_uom_qty. +# """ +# for record in self: +# if not record.picking_id or not record.product_id: +# continue + +# # Filter move lines in the related picking for the selected product +# moves = record.picking_id.move_ids_without_package.filtered( +# lambda move: move.product_id.id == record.product_id.id +# ) + +# if not moves: +# raise UserError(( +# "The product '%s' is not available in the related stock picking's moves. " +# "Please check and try again." +# ) % record.product_id.display_name) + +# # Calculate the total entries for the product in check.product for the same picking +# product_entries_count = self.search_count([ +# ('picking_id', '=', record.picking_id.id), +# ('product_id', '=', record.product_id.id) +# ]) + +# # Sum the product_uom_qty for all relevant moves +# total_qty_in_moves = sum(moves.mapped('product_uom_qty')) + +# # Compare the count of entries against the available quantity +# if product_entries_count > total_qty_in_moves: +# raise UserError(( +# "The product '%s' exceeds the allowable quantity (%s) in the related stock picking's moves. " +# "You can only add it %s times." +# ) % (record.product_id.display_name, total_qty_in_moves, total_qty_in_moves)) -- cgit v1.2.3 From b977df58492c18fbe0501e9e02cdee24c5e5176d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 8 Jan 2025 13:14:30 +0700 Subject: fix send email --- indoteknik_custom/models/stock_picking.py | 64 ++++++++++++++++++------------- 1 file changed, 37 insertions(+), 27 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 51899de9..2feb9c72 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -164,7 +164,7 @@ class StockPicking(models.Model): ('picking_type_code', '=', 'outgoing'), ('state', '=', 'done'), ('carrier_id', '=', 9) - ]) + ]) for picking in pickings: try: order_id = picking.lalamove_order_id @@ -796,27 +796,31 @@ class StockPicking(models.Model): self.calculate_line_no() self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' + return res + - template = self.env.ref('indoteknik_custom.mail_template_invoice_po_document') - if template and self.purchase_id: - # Render email body - email_values = template.sudo().generate_email( - res_ids=[self.purchase_id.id], - fields=['body_html'] - ) - rendered_body = email_values.get(self.purchase_id.id, {}).get('body_html', '') + def send_mail_bills(self): + if self.picking_type_code == 'incoming' and self.purchase_id: + template = self.env.ref('indoteknik_custom.mail_template_invoice_po_document') + if template and self.purchase_id: + # Render email body + email_values = template.sudo().generate_email( + res_ids=[self.purchase_id.id], + fields=['body_html'] + ) + rendered_body = email_values.get(self.purchase_id.id, {}).get('body_html', '') - # Render report dengan XML ID - report = self.env.ref('purchase.action_report_purchase_order') # Gunakan XML ID laporan - if not report: - raise UserError("Laporan dengan XML ID 'purchase.action_report_purchase_order' tidak ditemukan.") + # Render report dengan XML ID + report = self.env.ref('purchase.action_report_purchase_order') # Gunakan XML ID laporan + if not report: + raise UserError("Laporan dengan XML ID 'purchase.action_report_purchase_order' tidak ditemukan.") - # Render laporan ke PDF - pdf_content, _ = report._render_qweb_pdf([self.purchase_id.id]) - report_content = base64.b64encode(pdf_content).decode('utf-8') + # Render laporan ke PDF + pdf_content, _ = report._render_qweb_pdf([self.purchase_id.id]) + report_content = base64.b64encode(pdf_content).decode('utf-8') - # Kirim email menggunakan template - email_sent = template.sudo().send_mail(self.purchase_id.id, force_send=True) + # Kirim email menggunakan template + email_sent = template.sudo().send_mail(self.purchase_id.id, force_send=True) if email_sent: # Buat attachment untuk laporan @@ -829,16 +833,22 @@ class StockPicking(models.Model): 'mimetype': 'application/pdf', }) - # Tambahkan isi email dan laporan ke log note - self.purchase_id.message_post( - body=rendered_body, - subject="Pengiriman Email Invoice", - message_type='comment', - subtype_xmlid="mail.mt_note", - attachment_ids=[attachment.id], - ) + # Siapkan data untuk mail.compose.message + compose_values = { + 'subject': "Pengiriman Email Invoice", + 'body': rendered_body, + 'attachment_ids': [(4, attachment.id)], + 'res_id': self.purchase_id.id, + 'model': 'purchase.order', + } - return res + # Buat mail.compose.message + compose_message = self.env['mail.compose.message'].create(compose_values) + + # Kirim pesan melalui wizard + compose_message.action_send_mail() + + return True def action_cancel(self): if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': -- cgit v1.2.3